第十五届蓝桥杯大赛软件赛省赛C/C++大学B组

目录

🎬 云泽Q个人主页
🔥 专栏传送入口 : 《C语言》《数据结构》《C++》《Linux》《蓝桥杯系列

⛺️遇见安然遇见你,不负代码不负卿~


前言

大家好啊,我是云泽Q,欢迎阅读我的文章,一名热爱计算机技术的在校大学生,喜欢在课余时间做一些计算机技术的总结性文章,希望我的文章能为你解答困惑~

一、好数

好数

考察内容:模拟
解法

对于一个数x,拆分数的每一位,x不断模10,先提取最低位,再x除以10把最低位删掉,在提取的过程中可以定义一个变量i = 1来记录当前这一位是第几位,每次x % 10就看看是否与该变量同奇或同偶,若第一位是奇数,则说明与i = 1同奇,每次执行完x % 10, x / 10,让i++继续对比下一位

cpp 复制代码
#include<iostream>
using namespace std;

bool check(int x)
{
  int i = 1;
  while(x)
  {
    //对应位奇偶性相等
    if(((x % 10) % 2 != (i % 2))) return false;
    x /= 10;
    i++;
  }
  return true;
}

int main()
{
  int n; cin >> n;
  int ret = 0;
  for(int i = 1; i <= n; i++)
  {
    if(check(i))
      ret++;
  }
  cout << ret << endl;
  return 0;
}

二、R 格式

R格式


考察内容:高精度

这道题建议将下面的文字配合上面的图来看,细节太多,我就没有把文字内容备注到图里:

具体操作就是将浮点数d拆分每一位存入一个整数数组,然后依次乘n个2即可,不过这个过程中还有一些细节问题,这个浮点数还存在小数点,这个小数点需要特殊处理

因为这个浮点数的数据范围太大,读取浮点数用字符串来读,这里的小数点也会占一位

在将浮点数放入数组中有人可能会考虑到这种情况,1.25 × 2 × 2的过程中,每乘一次2会有小数点后的位数变小的情况,原来小数点后有两位,计算完之后得到5一位也没有了,这种情况并不用担心,可以先将这个1.25看作125,乘两次2之后就是500,这时候计算完再看之前1.25的小数点后有两位,此时就在最终结果倒数第二位这里加上一个小数点即可,和前面的计算结果是完全一样的,因此在用数组存浮点数的时候,并不关心小数点在哪里,只关心小数点后有多少位。再用数组存浮点数的时候直接将除小数点以外的数逆序存进去,用变量p标记小数点后有多少位,当用数组计算出最终结果的时候,例如这里p = 2,在第2位和第3位(数组从第一位开始计数)之间加上一个小数点即可

此时浮点数存入数组的时候还有一个细节问题,由于不存小数点,这里字符串下标与数组下标直接找对应关系是有问题的,比如字符串下标为5的位置对应数组下标为0的位置,下标值之和为5没有问题。字符串下标为4的位置对应数组下标为1的位置,下标值之和为5没有问题。字符串下标为2的位置对应数组下标为2的位置,下标值之和为4就存在问题了,所以逆序存储的时候还有一个小操作,先定义一个变量 i 从前往后遍历字符串数组,再定义一个变量 t 指向整型数组的最后一个位置,这个最后一个位置是可以通过计算得到的(先计算字符串的长度记为len = d.size() = 6,由于小数点不存,直接 -1 长度变为5,实际的数的个数是5个,这样整型数组最后一个数的下标就是t = len - 1,也就是t = 5 - 1 = 4),当i从下标0开始在字符串浮点数遍历的时候,当i每指向一个数填入到整型数组当中i++后,t也从最后一个位置开始向前减,比如i指向下标为0的位置2加入到t指向的下标为4的位置,i++,t- -。只要i指向的是一个数,比如现在指向3,将3填入后,重复前面的操作

直到i指向小数点位置(下标为3的位置,不是一个数了),此时只i++,t不- -,此时遍历到一个5的时候,再把这个5扔到t位置,然后i++,t- -

当遍历到最后一个数6的时候,将6放入t指向的位置,i++,t- -

此时就特殊处理的小数点的位置,就是多定义一个变量t。此时将所有的数存入整型数组中的时候,就变为了一个高精度 × 低精度的问题,在乘完n个2之后,由于p = 2,此时p指向下标为2对应4的位置,该位置的前一个位置5就是小数点后的最近一位,p位置指的是小数点前的最近一位4,若小数点后的最后一位大于5的话,p指向的位置再向前进一位即可

若p指向的位置是9且p指向位置的前一位 >= 5,此时9 + 1之后就要继续向前(3)进一位,p指向位置的后面位置也有可能有这种情况,所以也要处理这种细节情况

输出结果的时候就要从len - 1这里一直逆序输出到p,后面就不用管了,这里就是高精度浮点数 × 低精度的过程

再实现代码之前,先掏出计算器看这道题要开多大的数组

21000是300多位,d最多是1024位

结果最多是3.0×105位,这里数组大小我直接开1e6 + 10,绝对够用

cpp 复制代码
#include<iostream>
using namespace std;

const int N = 1e6 + 10;
int n;
string d;
int a[N], len, p;

void mul()
{
	//标记进位
	int c = 0;
	for (int i = 0; i < len; i++)
	{
		a[i] = a[i] * 2 + c;
		c = a[i] / 10;
		a[i] %= 10;
	}
	//最终可能还有一个进位,直接放到前一位
	if (c) a[len++] = c;
}

int main()
{
	cin >> n >> d;
	//len是去除小数点后的数组长度
	len = d.size() - 1;
	for (int i = 0, t = len - 1; i < d.size(); i++)
	{
		//若当前位是小数点,特殊标记
		//p标记小数点后有多少位
		if (d[i] == '.') p = d.size() - i - 1;
		//若当前位是数,逆序存入数组
		else a[t--] = d[i] - '0';
	}

	//乘 n 个2
	for (int i = 1; i <= n; i++) mul();

	//判断四舍五入
	if (a[p - 1] >= 5)
	{
		int c = 1;
		//进位若等于0,不用再加
		for (int i = p; i < len && c; i++)
		{
			a[i] = a[i] + c;
			c = a[i] / 10;
			a[i] %= 10;
		}
		if (c) a[len++] = c;
	}
	
	//输出结果,从最高位输出到p位
	for (int i = len - 1; i >= p; i--) cout << a[i];
	
	return 0;
}

三、拔河

拔河



考察内容:枚举 + set
【解法】

可以通过枚举右区间 [l,r] 的和 sum,然后在 [1,l−1] 内的所有区间中找出距离 sum 最近的区间长度。

那么,可以将 [1,l−1] 中的所有区间和信息存入 set 中(因为set这个数据结构是一个有序表),这样就可以在 log 时间内找出距离 sum 最近的区间。但是要注意,有可能比 sum 大,也有可能比 sum 小。

补充一下图中所说把[1, l - 1]里面所有区间和放入set中的详细原理过程

当枚举[l, r]这个区间的时候,如果使用两层for循环把左边所有的区间和放到set中,时间开销也是很爆炸的,和暴力枚举的时间开销是一样的,因此要做到在枚举右边区间的时候,快速的把左边这个区间的区间和全部扔到set中,此时就可以根据枚举的特点,假设枚举到[l, r]区间的时候,假设此时左边的区间和已经全部在set中

当枚举完[l, r]区间的时候,此时只需将l指向的位置向后移动一位,此时根据之前l的位置计算出来的左边的区间和依旧可以再用

无非就是新l向后移动一位之后,只添加了以这个旧l为结尾的区间,只要把新添加的这些区间和重新扔到set当中就可以了

并且这个时间复杂度仅需从后往前遍历一遍就可以做到

和把l枚举到新位置之后,再定义一个指针r从此位置向后枚举这个区间的时间开销等同,所以我们就可以在一边枚举右边区间,一边把新产生的区间和放入set中即可,也就可以说这个放入set的过程是不耗时的

cpp 复制代码
#include<iostream>
#include<set>
using namespace std;

const int N = 1e3 + 10;
typedef long long LL;
LL n;
LL a[N];
set<LL> mp;

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];

	//统计最终最小差距
	LL ret = 1e9;
	//枚举[l, r]区间
	for (int l = 2; l <= n; l++)
	{
		LL sum = 0;
		//把新产生的区间和,放入set中
		for (int i = l - 1; i >= 1; i--)
		{
			sum += a[i];
			mp.insert(sum);
		}
		sum = 0;
		//枚举右区间
		for (int r = l; r <= n; r++)
		{
			sum += a[r];
			//枚举完右边区间之后
			//在左边区间中快速找出离sum最近的区间和
			auto it = mp.lower_bound(sum);
			//若是可以找到
			if (it != mp.end()) ret = min(ret, *it - sum);
			//it指向的区间和前面必须还有一个区间和才可以支持--,否则越界
			//比如set中的区间和是7,7,9,10,it指向第一个7
			if (it != mp.begin())
			{
				it--;
				ret = min(ret, sum - *it);
			}
		}
	}
	cout << ret << endl;
	return 0;
}

结语

相关推荐
Wadli2 小时前
集群C++聊天服务器
服务器·开发语言·c++
luoqice2 小时前
利用flv库读取flv文件时长c程序
c语言·开发语言
洛水水2 小时前
# 线程池详解:从原理到实现
c++·线程池
大模型最新论文速读2 小时前
VQKV:KV Cache 压缩 82% 性能几乎不降
人工智能·深度学习·算法·机器学习·自然语言处理
yongui478342 小时前
基于MSP430和Zigbee技术的煤矿综合监控系统设计与实现
算法
Ww.xh2 小时前
ESP8266连接AI大模型完整指南
人工智能·算法·语言模型
思麟呀2 小时前
HTTP的Cookie和Session
linux·网络·c++·网络协议·http
小明同学012 小时前
linux进程(下)
linux·服务器·c++
汉克老师2 小时前
GESP2023年12月认证C++三级( 第一部分选择题(1-8))
c++·string·字符数组·gesp三级·gesp3级