网络知识 娱乐 2021蓝桥杯国赛B组C/C++个人记录

2021蓝桥杯国赛B组C/C++个人记录

以下为个人场上所提交答案,欢迎大家指出错误,后续会更正正确题解。




在这里插入图片描述

个人提交:25

说明: Mbps是M bit per second, MB是 M Byte,bit是比特,Byte是字节,1Byte=8bit, 200/8=25




在这里插入图片描述
个人提交:21844 (有误)
改正:1903

说明:只有每个位都为1、3、5、7之一才是题目所说的纯质数。还有一个条件当时没注意到,这个数本身也要是素数,应该先用埃氏筛法。

代码:

#include
#include

const int MAX_N = 20210605;
int num[MAX_N + 1];
int sum = 0;

void solve(int x) {
	int isPrime = 1;
	while(x > 0) {
		int b = x%10; //b为x的个位数字
		if(!(b == 2 || b == 3 || b == 5 || b == 7)) {
			isPrime = 0;
			break;
		}
		x = x/10;
	}
	if(isPrime) sum++;
}

int main() {
	memset(num, 1, sizeof(num));
	for(int i = 2; i < 20210605; i++) {
		for(int j = 2; i*j <= 20210605; j++) {
			num[i*j] = 0;
		}
	}
	for(int i = 1; i <= 20210605; i++) {
		if(num[i]) solve(i);
	}
	printf("%dn", sum);
	return 0;
}






在这里插入图片描述个人提交:977

说明:现场写日期类太麻烦,直接用excel搞数据,然后用c处理

在这里插入图片描述
先在excel中敲入这两格内容,第三格做个差,看看一共有多少天
在这里插入图片描述
在这里插入图片描述
得7699,从A1格往下拉7669行,得到从2001/1/1到2021/12/31的每一天
在这里插入图片描述为了方便处理数据,把刚才不用的删掉,并且把表格另存为.csv文件

在这里插入图片描述数据有了,直接全选复制,接下来用c/c++处理

#include
#include
#include
using namespace std;

int res = 0;

void solve(int x) {
	for(int i = 0; i < 100; i++) {
		if(i * i == x) res++;
	}
}

int main() {
	string line;
	while(getline(cin, line)) {
		int len = line.length();
		int sum = 0;
		for(int i = 0; i < len; i++) {
			if(isdigit(line[i])) sum += line[i] - '0';
		}
		solve(sum);
		cout << res << endl;
	}
}

得出结果977

赛后听了老师讲,其实遍历所有日期也不难

#include
#include

int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int main() {
	int cnt = 0;
	for(int year = 2001; year <= 2021; year++) {
		if(year%4==0 && year%100!=0 || year%400 == 0) days[2] = 29; //闰年29天 
		else days[2] = 28; 											//平年28天 
		for(int month = 1; month <=12; month++) {
			for(int day = 1; day <= days[month]; day++) {
				int total = 0;
				int t = year;
				while(t) {
					total += t % 10;
					t /= 10;
				}
				t = month;
				while(t) {
					total += t % 10;
					t /= 10;
				}
				t = day;
				while(t) {
					total += t % 10;
					t /= 10;
				}
				if((int)sqrt(total) * (int)sqrt(total) == total) {
					cnt++;
					printf("%d%02d%02d %2dn", year, month, day, total);
				}
			}
		}
	}
	printf("%dn", cnt);
	return 0;
}






在这里插入图片描述个人提交:2667336761

说明:赌了一把完全二叉树,可惜不对

分析:
一颗具有n个结点的最小权二叉树,假设其左子树有j个结点,其右子树的结点数为n-j-1,那么左子树一定是具有j个结点的最小权二叉树,右子树也一定是具有n-j-1个结点的最小权二叉树。–最优子结构性质

所以可以用动态规划来求解,转移方程即题目中的W(v) = 1 + 2W(L) + 3W(R ) + (C(L))2 C(R )

设 d[i]=有i个结点的二叉树的最小权值

dp[n] = min{1 + 2dp[j] + 3dp[n-j-1] + j2(n-j-1)}, j = 0, 1, 2, …, n-1

#include
#include
using namespace std;

long long dp[2030];

int main() {
	memset(dp, 0x3f, sizeof(dp)); //每个字节均为0x3f,即无穷大 
	dp[0] = 0;
	for(int i = 1; i < 2025; i++) {
		for(int j = 0; j <= i-1; j++) {
			if((1 + 2*dp[j] + 3*dp[i-j-1] + j*j*(i-j-1)) < dp[i]) {
				dp[i] = 1 + 2*dp[j] + 3*dp[i-j-1] + j*j*(i-j-1);
			}
		}
	}
	cout << dp[2021] << endl;
	return 0;
} 






在这里插入图片描述在这里插入图片描述
代码:

#include
#include
#include

char s[110];

int main() {
	scanf("%s", s);
	for(int i = 0; i < strlen(s); i++)
		printf("%c", toupper(s[i]));
	return 0;
}






在这里插入图片描述在这里插入图片描述在这里插入图片描述常规写法,过40%用例,评论区大佬说二分法,我在考场想到利用等差数列前n项和并根据R-L的值取分段函数,但是没来得及改,后期补上

#include

const int MAX_N = 200000000;
int a[MAX_N], sum = 0;

void solve() {
	int maxn = 1, n = 1;
	for(int i = 1; i < MAX_N; i++) {
		a[i] = n++;
		if(n > maxn) {
			maxn++;
			n=1;
		}
	}
}

int main() {
	solve();
	int T;
	scanf("%d", &T);
	for(int i = 0; i < T; i++) {
		int l, r;
		sum = 0;
		scanf("%d%d", &l, &r);
		for(int i = 0; l+i <= r; i++) {
			sum += a[l+i];
		}
		printf("%dn", sum);
	}
	return 0;
}

当时的思路对了一部分
分析:
将数列划分为n项,其中第1项为1,第2项为1,2,第3项为1,2,3,…,第i项为1,2,…,i。显然前i项共包含了i(i+1)/2个数,最多需要不超过2*106项,即可表示数列的前1012个数。
定义f[i]表示第i项的和,sum[i]表示前i项的和.则

f[i] = i*(i+1)/2;
sum[i] = i*(i+1)*(i+2)/6;

定义函数cal(x)表示数列第1个数~第x个数的和,那么区间 [L, R] 的和就等于 cal(R ) - cal(L-1)。
而对于cal(x),我们设x在第i项第j个位置,那么可得:cal(x) = sum(i-1) + j(j+1)/2
如何求得i和j呢,随着i增大,i*(i+1)/2的值也增大,直到 i*(i+1)/2大于x,即可求出i,可以采用二分法求出i。已知i后,j可以一步算出,即 j = x - (i-1)(i)/2
时间复杂度为O(Tlogn)

#include
using namespace std;

long long sum(long long i) {
	return i*(i+1)*(i+2)/6;
}

long long cal(long long x) {
	long long left = 0, right = 2e6, i;
	//找到最大的i,满足 i*(i+1)/2 <= x,即找到x的前一个项 
	while(left <= right) {
		long long mid = left + right >> 1;
		if(mid*(mid+1)/2 <= x) {
			left = mid + 1; i = mid; //因为递增求i,所以当区间左边增大时赋值 
		} else {
			right = mid - 1;
		}
	}
	long long j =  x-i*(i+1)/2;
	return sum(i) + j*(j+1)/2;
}

int main() {
	long long T = 1;
	cin >> T;
	while(T --) {
		long long l, r;
		cin >> l >> r;
		cout << cal(r) - cal(l-1) << endl;
	}
	return 0;
}






在这里插入图片描述在这里插入图片描述在这里插入图片描述没找到规律,硬写,过40%用例
评论区大佬说找循环节,后期补上

#include
#include

const int MAX_N = 200000000;
char s[MAX_N];
char s2[MAX_N];
int n, t;

void reverse() {
	s2[0] = s[0];
	for(int i = 1; i < n; i++) {
		s2[i] = (s[i] - '0') ^ (s[i-1] - '0') + '0';
	}
	strncpy(s, s2, n);
}

int main() {
	scanf("%d%d", &n, &t);
	scanf("%s", s);
	for(int i = 0; i < t; i++) reverse();
	printf("%s", s);
	return 0;
}

分析:
这道题并没有考察特别难的算法,其实多模拟一些输出可以找到循环节。
将题目案例进行16次变换

10110  
11101
10011  // 10
11010  
10111  // 1011
11100
10010
11011  
10110  // 10110 进入下一次循环
11101
10011
11010
10111
11100
10010
11011
10110
10110

经过观察我们可以发现:

前 1 位数字在异或 a 次后等于输入本身

前 2 位数字在异或 2a 次后等于输入本身

前 3 为数字在异或 4a 次后等于输入本身

前 4 位数字在异或 4a 次后等于输入本身

前 5 位数字在异或 8a 次后等于输入本身

前 6 位数字在异或 8a 次后等于输入本身

前 7 位数字在异或 8a 次后等于输入本身

前 8 位数字在异或 8a 次后等于输入本身

前 9 位数字在异或 16a 次后等于输入本身

可以得出,前 n 位数字在变换 2k 次后恢复初始,最小的k满足n <= 2k
所以将题目中的t去掉循环部分,求剩余部分次数的变换,可以节省大量算法复杂度,题目最大的n为10000,最大的t为1e18,最多刚好进行 10000 * 10000 次运算,可以满足时间限制1s的要求。

#include
#include

long long s[10010];
long long n, t;

void printstr() {
	for(int i = 0; i < n; i++) {
		printf("%d", s[i]);
	}
	printf("n");
}

int main() {
	scanf("%d%dn", &n, &t);
	for(int i = 0; i < n; i++) {
		s[i] = getchar() == '1' ? 1 : 0;
	}
	int len, x; 
	if(pow(2, (int)log(n)/log(2))  == n) {
		// 2的n次幂 == log(n) / log(2) ,即log2(n)为整数 
		x = (int)(log(n)/log(2));
		len = pow(2, x);
	} else {
		x = (int)(log(n)/log(2)) + 1; //上取整
		len = pow(2, x);
	}
	t = t % len;
	for(int i = 0; i < t; i++) {
		for(int j = n; j > 0; j--) {
			s[j] = s[j] ^ s[j-1];
		}
	}
	printstr();
	return 0;
}






在这里插入图片描述在这里插入图片描述不会dp,硬写过30%用例

#include
using namespace std;

int N, K, res = 0;

void solve(int x) {
	int sum = 0;
	while(x > 0) {
		if(x % 2 == 1) sum++;
		x = x/2;
	}
	if(sum == K) res++;
}

int