2021第十二届蓝桥杯B组省赛C/C++做题过程与反思

2021第十二届蓝桥杯B组省赛C/C++做题过程与反思

​ 鄙人大一,第一次有幸参加蓝桥杯b组c。以下是本人的做题全过程,注意了!!!!不一定是正确答案!! 如果有误,还请大佬们指点指点。

试题 A: 空间

【问题描述】

小蓝准备用 256MB 的内存空间开一个数组,数组的每个元素都是 32 位

二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问

256MB 的空间可以存储多少个 32 位二进制整数?

思路

当时想错了,想都没想直接就除了32…答案应该是 67108864(256 * 1024 * 1024 / 4) 。

试题 B: 卡片

【问题描述】

小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。

小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,

就保存起来,卡片就不能用来拼其它数了。

小蓝想知道自己能从 1 拼到多少。

例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,

但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。

现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1

拼到多少?

思路

做的时候,信誓旦旦打出代码,提交答案 3832 赛后看到别人 3831 ???王德发??好吧是我没减1…草率了。

#include 
using namespace std;
int a[10];
bool judge(int i)
{
     
		while(i){
     
			if(a[i%10]>=1){
     
				a[i%10]--;
			}
			else return 0;
			i/=10;
		}
		return 1;
}
int main()
{
     
		for(int i=0;i<=9;i++){
     
			a[i]=2021;
		}
		for(int i=1;i<=100000;i++){
     
			if(judge(i)){
     
				;
			}
			else {
     
				cout<<i-1;
				break;
			}
		}
		return 0;
} 

试题 C: 直线

【问题描述】

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,

那么这些点中任意两点确定的直线是同一条。

给定平面上 2 × 3 个整点 {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},即横坐标

是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数

的点。这些点一共确定了 11 条不同的直线。

给定平面上 20 × 21 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横

坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之

间的整数的点。请问这些点一共确定了多少条不同的直线。

思路

​ 这题做的时候呢,一开始是想手写看看能不能写出来,推了有那么一段时间,用排列组合,然后排除同x同y的,最后发现…不对劲啊,斜着也可以重合。。。然后突然想到19年还是20年有一题是线分成多少个面的…要暴力存之前的线,然后发现套在这个题上也可以,所以就有了以下的思路。

直接开个结构体数组存每条线的k,b值最后看结构体数组的大小就是答案了,。但是写的时候有个问题,垂直于x,y的时候怎么处理?然后我就想,直接不处理了,画图发现垂直于x的不就是20条,垂直于y的不就是21条。那好办了,于是就有了下面的代码。答案应该是 40257

#include 
using namespace std;
struct node {
     
	double k,b;
}s[1000000];
int p; //线的个数
double k,b; 
void f(int x1,int y1,int x2,int y2)
{
     
		k=(double)(y1-y2)/(double)(x1-x2);//不用考虑x1==x2,y1==y2了 
		b=(double)(y1*(x1-x2)-x1*(y1-y2))/(x1-x2); //手推公式 
}
int main()
{
     
		int n=19,m=20;
		for(int x1=0;x1<=n;x1++){
     
			for(int y1=0;y1<=m;y1++){
     
				for(int x2=0;x2<=n;x2++){
     
					for(int y2=0;y2<=m;y2++){
     
						if(x1==x2||y1==y2){
      //特殊处理,在答案直接加垂直于x,y的线,所以不用算 
							continue;
						}
						f(x1,y1,x2,y2); //计算当前两点的k,b值
						int flag=1;
						for(int i=1;i<=p;i++){
     
							if(k==s[i].k&&b==s[i].b){
     
								flag=0;
								break;
							}
						} 
						if(flag){
       //如果之前没有这个线,就可以存起来 
							s[++p].k=k;
							s[p].b=b;
						}
					}
				}
			}
		}
		cout<<p+20+21;//加上垂直的答案; 
		return 0;
} 

试题 D: 货物摆放

【问题描述】

小蓝有一个超大的仓库,可以摆放很多货物。

现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝

规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、

宽、高。

小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上

分别堆 LWH 的货物,满足 n = L × W × H

给定 n,请问有多少种堆放货物的方案满足要求。

例如,当 n = 4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、

2 × 2 × 1、4 × 1 × 1。

请问,当 n = 2021041820210418 (注意有 16 位数字)时,总共有多少种

方案?

思路

​ 一开始想着能不能暴力以下??估计大家都有这样的想法吧,当然了,肯定跑不出来的,这么大的数。然后我就往下看了,最后才回来做的,我发现好像有个规律,找三个数相乘等于一个数,其实这三个数都是这个数的因子。所以思路就出来了,找出n的所有因子,三层循环暴力一下组合方法就行了。答案应该是 2430

#include 
#define ll long long
using namespace std;
set <ll > s;  //用set能避免去重的麻烦操作 
ll a[100000],p;  //把set的数存到数组,又能化简遍历难度。 
int main()
{
     
		ll n=2021041820210418;
		for(ll i=1;i<=sqrt(n);i++){
       //开方优化
			if(n%i==0){
     
				s.insert(i); //无脑放就行,反正能去重 
				s.insert(n/i);
			}
		}
		set <ll > :: iterator it;
		for(it=s.begin();it!=s.end();it++){
     
			a[++p]=*it;
		}
		int co=0;
		for(int i=1;i<=p;i++){
     
			for(int j=1;j<=p;j++){
     
				for(int k=1;k<=p;k++){
     
					if(a[i]*a[j]*a[k]==n){
     
						co++;
					}
				}
			}
		}
		cout<<co;
		return 0;
} 

试题 E: 路径

【问题描述】

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图

中的最短路径。

小蓝的图由 2021 个结点组成,依次编号 1 至 2021。

对于两个不同的结点 a, b,如果 ab 的差的绝对值大于 21,则两个结点

之间没有边相连;如果 ab 的差的绝对值小于等于 21,则两个点之间有一条

长度为 ab 的最小公倍数的无向边相连。

例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无

向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。

请计算,结点 1 和结点 2021 之间的最短路径长度是多少。

思路

​ 这个应该蛮容易想到的,就是图论嘛,根据题意存边,存完之后跑一次弗洛伊德就行了,虽然复杂度高,但是代码简洁,用dij什么也行。代码稍微复杂一点。答案应该是 10266837

#include 
#define ll long long
using namespace std;
int G[2050][2050];
int INF=200000000;
int gcd(int a,int b)
{
     
		if(b==0)return a;
		return gcd(b,a%b);
}
int main()
{
     
		for(int i=1;i<=2021;i++){
     
			for(int j=1;j<=2021;j++){
     
				G[i][j]=INF;
			}
		}
		for(int i=1;i<=2021;i++){
       //按题意建图 
			for(int j=1;j<=2021;j++){
     
				if(abs(i-j)<=21){
     
					int lcm=i*j/gcd(i,j); //最小公倍数 
					G[i][j]=min(G[i][j],lcm); //注意不要直接存,会有重边 
				}
			}
		}
		for(int k=1;k<=2021;k++){
       //弗洛伊德模板 
			for(int i=1;i<=2021;i++){
     
				if(G[i][k]==INF)continue;  //小小优化 
				for(int j=1;j<=2021;j++){
     
					if(G[k][j]==INF)continue;
					G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
				}
			}
		}
		cout<<G[1][2021];
		return 0;
} 

试题 F: 时间显示

【问题描述】

小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取

了当前的时间,用一个整数表示,值为从 1970 年 1 月 1 日 00:00:00 到当前时

刻经过的毫秒数。

现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要

显示出时分秒即可,毫秒也不用显示,直接舍去即可。

给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

【输入格式】

输入一行包含一个整数,表示时间。

【输出格式】

输出时分秒表示的当前时间,格式形如 HH:MM:SS,其中

HH 表示时,值

为 0 到 23,MM 表示分,值为 0 到 59,SS 表示秒,值为 0 到 59。时、分、秒

不足两位时补前导 0。

【样例输入 1】

46800999

【样例输出 1】

13:00:00

【样例输入 2】

1618708103123

【样例输出 2】

01:08:23

思路

​ 这个题总体看上去不难,但是一开始我不知道毫秒跟秒怎么换算,最后是看第一个样例得出来的结果!!! 1秒等于1000毫秒,这道题比较好人,可以直接去掉毫秒,也就是一开始的n让它除以1000就行,然后得出来的结果是多少秒,然后在用多少秒除以60得出多少分钟,然后多少分钟除以60得出多少时,然后时,分,秒,都各自取模就好。注意一下输出,就行。

#include 
#define ll long long
using namespace std;
int main()
{
     
		ll n;
		cin>>n;
		n/=1000;
		ll s,f,m;//s是时,f是分,m是秒
		m=n;
		f=m/60;
		s=f/60;
		m%=60;
		f%=60;
		s%=24;
		if(s<=9){
     
			cout<<0<<s;
		}
		else cout<<s;
		cout<<':';
		if(f<=9){
     
			cout<<0<<f;
		}
		else cout<<f;
		cout<<':';
		if(m<=9){
     
			cout<<0<<m;
		}
		else cout<<m;
		return 0;
} 

试题 G: 砝码称重

【问题描述】

你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · , W**N

请你计算一共可以称出多少种不同的重量?

注意砝码可以放在天平两边。

【输入格式】

输入的第一行包含一个整数 N

第二行包含 N 个整数:W1, W2, W3, · · · , W**N

【输出格式】

输出一个整数代表答案。

【样例输入】

3

1 4 6

【样例输出】

10

思路

一眼看上去,呦,这不是dp嘛,第二眼看上去,确认过眼神…01背包?嗯,不太像 有点特别,多了个相减,不过道理是不变的 ,但是dp的条件不知道有没有写齐全,感觉有点问题。。。 用dfs应该能过一些点,但是应该会超时。

#include 
#define ll long long
using namespace std;
int dp[105][100005];  //数据保证N个砝码总重不超过 100000 
int n;
int v[105];
int m;  //所有的重量加起来,方便dp的第二层循环 
int main()
{
     
		cin>>n;
		for(int i=1;i<=n;i++){
     
			scanf("%d",&v[i]);
			m+=v[i];
		}
		for(int i=0;i<=n;i++){
       //预处理0处的值 
			dp[i][0]=1;
		}
		for(int i=1;i<=n;i++){
     
			for(int j=1;j<=m;j++){
     
				if(dp[i-1][j]==1){
       //不要v[i]的情况 
					dp[i][j]=1;
				}
				else if(j>=v[i]&&dp[i-1][j-v[i]]==1){
      //加一个v[i]的情况 
					dp[i][j]=1;
				}
				else if(j+v[i]<=m&&dp[i-1][j+v[i]]==1){
      //减一个v[i]的情况 
					dp[i][j]=1;
				}
				else if(v[i]>=j&&dp[i-1][v[i]-j]==1){
      //把v[i]放对面的情况 
					dp[i][j]=1;
				}
			}
		}
		int co=0;
		for(int i=1;i<=m;i++){
     
			if(dp[n][i]==1){
     
				co++;
			}
		}
		cout<<co;
		return 0;
} 

试题 H: 杨辉三角形

【问题描述】

下面的图形是著名的杨辉三角形:

2021第十二届蓝桥杯B组省赛C/C++做题过程与反思_第1张图片

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下

数列:

1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, …

给定一个正整数 N,请你输出数列中第一次出现

N 是在第几个数?

【输入格式】

输入一个整数 N

【输出格式】

输出一个整数代表答案。

【样例输入】

6

【样例输出】

13

思路

没啥思路,4000以下纯暴力,( 多了会超时 可能或许5000也行,但是我没冒险 )4000以上当做n在第n+1排的第二个处理,反正质数肯定是满足的,其他数应该也有不少满足的 用等差数列前n项和公式就行,公差为1,Sn = n+(n*(n-1))/2

#include 
#define ll long long
using namespace std;
int G[5005][5005];
int n;
int main()  
{
     
		cin>>n;
		if(n>4000){
        //或许能骗分 
			cout<<n+(n*(n-1))/2+2;
			return 0;
		}
		G[1][1]=1;
		for(int i=2;i<=n+1;i++){
     
			G[i][1]=1;
			for(int j=2;j<=i;j++){
     
				G[i][j]=G[i-1][j]+G[i-1][j-1];
			}
		}
		int now=0;  //自增个数 
		for(int i=1;i<=n+1;i++){
     
			for(int j=1;j<=i;j++){
     
				now++;
				if(n==G[i][j]){
       //遇到第一个就是答案 
					cout<<now;
					return 0;
				}
			}
			//cout<
		}
		return 0;
} 

试题 I: 双向排序

【问题描述】

给定序列 (a1, a2, · · · , a**n) = (1, 2, · · · , n),即 a**i

= i

小蓝将对这个序列进行 m 次操作,每次可能是将 a1, a2, · · · , aqi 降序排列,

或者将 aqi , aqi+1, · · · , a**n 升序排列。

请求出操作完成后的序列。

【输入格式】

输入的第一行包含两个整数 n, m,分别表示序列的长度和操作次数。

接下来 m 行描述对序列的操作,其中第 i 行包含两个整数 p**i, q**i 表示操作

类型和参数。当 p**i = 0 时,表示将 a1, a2, · · · , aqi 降序排列;当 p**i = 1 时,表示

aqi , aqi+1, · · · , a**n 升序排列。

【输出格式】

输出一行,包含 n

个整数,相邻的整数之间使用一个空格分隔,表示操作

完成后的序列。

【样例输入】

3 3

0 3

1 2

0 2

【样例输出】

3 1 2

思路

没有思路…只能用sort纯模拟 能过60%的点…

#include 
#define ll long long
using namespace std;
int a[100005];
int n,m;
bool cmp(int a,int b)
{
     
		return a>b;
}
int main()  
{
     
		cin>>n>>m;
		for(int i=1;i<=n;i++){
     
			a[i]=i;
		}
		for(int i=1;i<=m;i++){
     
			int p,q;
			scanf("%d%d",&p,&q);
			if(p==0){
     
				sort(a+1,a+1+q,cmp);
			}
			else {
     
				sort(a+q,a+1+n);
			}
		}
		for(int i=1;i<=n;i++){
     
			if(i!=1){
          //不知道会不会卡空格 
				cout<<" ";
			}
			cout<<a[i];
		}
		return 0;
} 

试题 J: 括号序列

【问题描述】

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,

当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。

两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括

号。

例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几

种不同的添加结果:()()()、()(())、(())()、(()()) 和 ((()))。

【输入格式】

输入一行包含一个字符串 s,表示给定的括号序列,序列中只有左括号和

右括号。

【输出格式】

输出一个整数表示答案,答案可能很大,请输出答案除以

1000000007 (即

109 + 7) 的余数。

【样例输入】

((()

【样例输出】

5

思路

没思路,也没时间想。 草率了,空了,样例都没输出一下,复制出来纯粹是为了 给有兴趣的人看一下

总结

其他感觉还行,算是正常发挥了吧,能做的都做了,不会的也依旧不会,最气人的是第一第二题都错了!!!!今年的体验感还行,明年继续。

你可能感兴趣的:(2021第十二届蓝桥杯B组省赛C/C++做题过程与反思)