当前位置: 首页 > news >正文

第十六届蓝桥杯 C/C++ B组 题解

        做之前的真题就可以发现,蓝桥杯特别喜欢出找规律的题,但是我还是低估了官方的执念。本博客用于记录第一次蓝桥的过程,代码写的很烂,洛谷已经有的题解,这里不再赘述,只说自己遇到的问题。用于以后回顾和查找资料,毕竟自己总结的印象更深。

        本博客,没有多少借鉴价值,基本全是暴力破解和吐槽。

试题 A: 移动距离  (填空 5分)

        洛谷链接:P12130 [蓝桥杯 2025 省 B] 移动距离 - 洛谷

       我不会,豆包说“在平面直角坐标系中,从原点(0,0)到点(x,y) ,如果先沿x轴正方向移动到(x,0) ,再沿以原点为圆心,半径为x2+02​=x的圆移动到(x,y) ,这种移动方式的总距离最短。” ,但是我没有理解。

豆包代码:

#include <iostream>
#include <cmath>
using namespace std;int main() {double x = 233;double y = 666;double r = sqrt(x * x + y * y);double distance = r + r * atan(y / x);cout << distance << endl;// 四舍五入到整数int result = round(distance);cout << result << endl;return 0;
}

试题 B: 客流量上限(填空 5分)

        洛谷链接:P12131 [蓝桥杯 2025 省 B] 客流量上限 - 洛谷

         比赛的时候,感觉是有什么规律的,我一直在思考如何保证2025个分店的客流量上限不同,感觉dfs可以,但是又不知道如何判断A_iA_j 的乘积不得超过 i × j + 2025,故作罢。

         在洛谷上,看到大佬的题解,思路特别好:(P12131 [蓝桥杯 2025 省 B] 客流量上限 - 洛谷):

#include <bits/stdc++.h>
using namespace std;int main(){int n;cin >> n;vector<int> a;for(int i=1;i<=n;i++){a.push_back(i);}long long cnt=0;do{bool flag=true;for(int i=0;i<n;i++){//位置for(int j=0;j<n;j++){//位置if(a[i]*a[j]>(i+1)*(j+1)+n){//printf("%d %d\n",a[i],a[j]);flag=false;i=n;break;}}}if(flag){cnt++;cnt%=1000000007;}}while(next_permutation(a.begin(),a.end()));cout << cnt;
}

        1        1

        2        1

        3        2

        4        2

        5        4

        6        4

        7        8

        8        8

 “1、1、2、2、4、4、8、8”,发现次数等于$2^{\lfloor(n - 1)/2\rfloor}$

        只需要再根据规律,算出结果即可。 

#include<bits/stdc++.h>
using namespace std;
int main() {int n;cin >> n;long long flag=(n-1)/2;long long sum=1;for (int i = 0; i < flag; i++){sum*=2;sum%=1000000007;}cout << sum << endl;return 0;
}

试题 C: 可分解的正整数(10分)

洛谷链接:P12132 [蓝桥杯 2025 省 B] 可分解的正整数 - 洛谷

        

【输入格式】

输入的第一行包含一个正整数 N ,表示数据的个数。
第二行包含 N 个正整数 A 1 , A 2 , . . . , A N ,表示需要判断是否可分解的正整数
序列。

【输出格式】

输出一个整数,表示给定数据中可分解的正整数的数量。

【样例输入】 

3
3 6 15

【样例输出】

3

【样例说明】

A i = 3 是可分解的,因为 [0 , 1 , 2] 的和为 0 + 1 + 2 = 3
A i = 6 是可分解的,因为 [1 , 2 , 3] 的和为 1 + 2 + 3 = 6
A i = 15 是可分解的,因为 [4 , 5 , 6] 的和为 4 + 5 + 6 = 15
所以可分解的正整数的数量为 3

【评测用例规模与约定】

对于 30 % 的评测用例, 1 N 100 1 A_i   100
对于 100 % 的评测用例, 1 N 10^5 1 A_i   10^9

        这道题再知乎上也被很多人吐槽了,除了1全部可以。我的思路是从找规律开始,找他的样例:

0+1+2 = 3

1+2+3 = 6

2+3+4 = 9

会发现长度为三的序列可以输出所有三的倍数,所以只要是3的倍数都可以分解。 

        但是要注意题目条件是“序列长度至少为3”,那如果长度为4,就是4的倍数也全部可分解,长度为5,就是5的倍数可以分解......这样下去,岂不是所有正整数都可以。恰好 “1 A_i 10^9”。

        而且可以有负数,那么...:

-1 + 0 + 1 + 2 = 2

-2 + -1 + 0 + 1 + 2 + 3 = 3

        这样下去就是除了1,都可以“因为不能“-1+0+1+1”。

        故找到规律后代码就很简单了,相信大家都知道怎么写了,但是我犯了一个很蠢的错误:

#include <bits/stdc++.h>
using namespace std;int main()
{int n=0;long long flag=0;scanf("%d",&n);for(int i=0;i<n;i++){scanf("%lld",&flag);if(flag==1)n--; }printf("%d",n);return 0;
}

        大家可以看一下是什么问题,下面是通过的代码:

#include <bits/stdc++.h>
using namespace std;int main()
{int n=0;long long flag=0;scanf("%d",&n);for(int i=n;i>0;i--){scanf("%lld",&flag);if(flag==1)n--; }printf("%d",n);return 0;
}

 试题 D: 产值调整(10分)

洛谷链接:P12133 [蓝桥杯 2025 省 B] 产值调整 - 洛谷

【输入格式】

输入的第一行包含一个整数 T ,表示测试用例的数量。
接下来的 T 行,每行包含四个整数 A B C K ,分别表示金矿、银矿和
铜矿的初始产值,以及需要执行的调整次数。

【输出格式】

对于每个测试用例,输出一行,包含三个整数,表示经过 K 次调整后金
矿、银矿和铜矿的产值,用空格分隔。

【样例输入】

2
10 20 30 1
5 5 5 3

【样例输出】

25 20 15
5 5 5

【评测用例规模与约定】

对于 30 % 的评测用例, 1 T 100 1 A , B , C , K 10^5
对于 100 % 的评测用例, 1 T 10 5 1 A , B , C , K 10^9

        看到这道题,思路不难,那就可能会出现超时问题,那就代表可能有规律,但是我没看出来,所以就先写出暴力解法看看:

#include <bits/stdc++.h>
using namespace std;int main()
{int n;scanf("%d",&n);long long A,B,C,K;for(int j=0;j<n;j++){scanf("%lld%lld%lld%lld",&A,&B,&C,&K);int flag_A=A;int flag_B=B;int flag_C=C;for(int i=0;i<K;i++){A=(flag_B+flag_C)/2;B=(flag_A+flag_C)/2;C=(flag_A+flag_B)/2;flag_A=A;flag_B=B;flag_C=C;}printf("%lld %lld %lld\n",A,B,C);		}return 0;
}

         运行例题通过了,那么就稍微改一下样例试试,加大看看是否超时:

2
10 20 30 50
5 5 5 30

        结果为:

18 18 18

5 5 5

        !!发现两组数A、B、C都相同,再让它输出中间值发现,除了前几次运算A、B、C不同,后边运算全相同,再换几个数也发现同样的规律。

        故我想到的便是在三个数相同的时候跳出循环,这就是本题的答案:

#include <bits/stdc++.h>
using namespace std;int main()
{int n;scanf("%d",&n);long long A,B,C,K;for(int j=0;j<n;j++){scanf("%lld%lld%lld%lld",&A,&B,&C,&K);int flag_A=A;int flag_B=B;int flag_C=C;for(int i=0;i<K;i++){A=(flag_B+flag_C)/2;B=(flag_A+flag_C)/2;C=(flag_A+flag_B)/2;flag_A=A;flag_B=B;flag_C=C;if(A==B&&B==C&&A==C){
//				printf("yes\n");break;}//			printf("A=%d B=%d C=%d\n",A,B,C);}printf("%lld %lld %lld\n",A,B,C);		}return 0;
}

试题 E: 画展布置 (15分)

        洛谷链接:P12134 [蓝桥杯 2025 省 B] 画展布置 - 洛谷

         

【输入格式】

输入共两行。
第一行包含两个正整数 N M ,分别表示画作的总数和需要挑选的画作数量。
第二行包含 N 个正整数 A 1 , A 2 , . . . , A N ,表示每幅画作的艺术价值。

【输出格式】

输出一个整数,表示 L 的最小值。

【样例输入】

4 2

1 5 2 4

【样例输出】

3

【评测用例规模与约定】

对于 40 % 的评测用例, 2 M N 10 3 1 A i 10^3
对于 100 % 的评测用例, 2 M N 10 5 1 A i 10^5

         这道题思路洛谷上题解也已经讲的很清楚(P12134 [蓝桥杯 2025 省 B] 画展布置 - 洛谷),我再做的时候遇到一个问题:

#include <bits/stdc++.h>
using namespace std;int data[100004]={0};int main()
{int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&data[i]); sort(data,data+n+1);// for(int i=1;i<=n;i++)// 	printf("%d ",data[i]);// printf("\n");long long sum=0,flag_min=0;flag_min=data[m]*data[m]-data[1]*data[1];for(int i=2;i+m-1<=n;i++){// flag_min = min(flag_min,data[i+m-1]*data[i+m-1]-data[i]*data[i]);sum=data[i+m-1]*data[i+m-1]-data[i]*data[i];if(sum<flag_min)flag_min=sum;}	printf("%lld",flag_min);return 0;
}

        这套代码再VScode会报错的:“"data" 不明确”,这是因为头文件中包含了同名的函数“data”造成的,所以要把“int data[100004]={0};”放在main函数里面,或者重命名。

        另外他也不能通过全部测试点:

        这是因为类型问题: data是“int”型,而sum和flag_min是long long型;data改为“long long”类型,便全部通过了:

#include <bits/stdc++.h>
using namespace std;int main()
{long long data[100004]={0};int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&data[i]); sort(data,data+n+1);// for(int i=1;i<=n;i++)// 	printf("%d ",data[i]);// printf("\n");long long sum=0,flag_min=0;flag_min=data[m]*data[m]-data[1]*data[1];for(int i=2;i+m-1<=n;i++){flag_min = min(flag_min,data[i+m-1]*data[i+m-1]-data[i]*data[i]);}	printf("%lld",flag_min);return 0;
}

试题 F: 水质检测(15分)

【输入格式】

输入共两行,表示一个 2 × n 的河床。
每行一个长度为 n 的字符串,仅包含 ‘#’ ‘.’ ,其中 ‘#’ 表示已经存在的
检测器, ‘.’ 表示空白。

【输出格式】

输出共 1 行,一个整数表示答案。

【样例输入】

.##.....#
.#.#.#...

【样例输出】

5

【样例说明】

其中一种方案:
.###....#

.#.######
增加了 5 个检测器

【评测用例规模与约定】

对于 100 % 的评测用例,保证 n 1000000

         对于这道题,我一开始想到的是暴力,通过dfs,第一个水质检测器作为起点,最后一个作为终点。比赛的时候是能过案例,同时我也自己试了几组数据也都是通过的,我当时想到的可能会有几个超时的,毕竟是遍历。但是我在洛谷运行的时候,竟然有WA。:

        代码如下: 

#include <bits/stdc++.h>
using namespace std;string str[2]={"\0"};
int flag_min=0;
int book[2][1000004]={0};
//int sum=0;
int beg_x=0,beg_y=0;
int end_x=0,end_y=0;
int up[2][2]={0,1,/*右*/1,0	/*下*/
};void dfs(int x,int y,int sum)
{if(x==end_x&&y==end_y){
//		printf("sum=%d end!\n",sum);if(sum<flag_min)flag_min=sum;return ;}else if(x>=2||y>=str[0].size())	return ;if(book[x][y]==0)for(int i=0;i<2;i++){book[x][y]=1;if(str[x][y]=='.')sum++; dfs(x+up[i][0],y+up[i][1],sum);book[x][y]=0;}return ;
} int main()
{char c;int n=0;cin>>str[0]>>str[1];flag_min=str[0].size()*2;int flag=0;for(int i=0;i<str[0].size();i++){for(int j=0;j<2;j++){
//			printf("begin_test!\n");if(str[j][i]=='#'){flag=1;beg_x=j;beg_y=i;
//				printf("%d %d\n",beg_x,beg_y);break;}		}if(flag==1)	break;}if(flag==0) printf("0");/*表示一个#都没有*/else{flag=0;for(int i=str[0].size()-1; i>=0;i--){for(int j=1;j>=0;j--){
//				printf("end_test!\n");if(str[j][i]=='#'){flag=1;end_x=j;end_y=i;
//					printf("%d %d\n",end_x,end_y);break;}		}if(flag==1)	break;}//	/*min_road*/dfs(beg_x,beg_y,0);printf("%d",flag_min);}return 0;
}

        先找出起终点,然后通过dfs遍历,像迷宫问题一样(万能搜索算法-CSDN博客) ,TLE在预料之中,但WA不再,所以我开始找问题,最后发现,问题出在dfs的遍历方向上,我只遍历了“左”,“上”连个方向,这种情况只适用于,起点在第一行的情况,但如果出现在第二行,便永远不能到达第一行了,所以我又加了一个向上的方向。

int up[3][2]={{0,1},	/*右*/{1,0},	/*下*/{-1,0}	/*上*/
};

        得到如下结果:

         在预料之中。洛谷大佬已经给出了正解:P12135 [蓝桥杯 2025 省 B] 水质检测 - 洛谷。这种解法我是真想不出来,所以便不强行解释了。目前就暴力解法了。

试题 G: 生产车间(20分)

        洛谷链接:P12136 [蓝桥杯 2025 省 B] 生产车间 - 洛谷

【输入格式】

输入共 n + 1 行。
第一行为一个正整数 n
第二行为 n 个由空格分开的正整数 w 1 , w 2 , ..., w n
后面 n 1 行,每行两个整数表示树上的一条边连接的两个结点。

【输出格式】

输出共一行,一个整数代表答案。

【样例输入】

9
9 7 3 7 1 6 2 2 7
1 2
1 3
2 4
2 5
2 6
6 7
6 8
6 9

【样例输出】

8

【样例说明】

删掉结点 4 9 后生产线满足条件,根结点 1 每单位时间将打包出 8 单位
的成品。

【评测用例规模与约定】

对于 20 % 的评测用例, 2 n 100
对于 100 % 的评测用例, 2 n 1000 w i 1000

         这道题的树就已经难到我了,因为我当时已经忘了怎么构建树了,而且这是一个多叉树,还不是二叉树。所以考试的时候是通过邻接矩阵 加01背包构造的,代码如下:

#include<bits/stdc++.h>
using namespace std;int dfs(int root,int x,vector<vector<int>> tree,vector<int> w,int n)
{	vector<vector<int>> data(x,vector<int>(w[root]+1,0));/*0-x*/int first=0;for(int j=1;j<=n;j++){if(tree[root][j]==2){first=j;break;}		}for(int i=0;i<=w[root];i++){if(i>w[i])data[0][i]=w[i];}if(x==n)for(int i=0;i<x;i++){for(int j=0;j<=w[root];j++)if(i<w[i])data[i][j]=data[i-1][j];elsedata[i][j]=max(data[i-1][j],data[i-1][j-w[i]]+w[i]);}return data[x-1][w[root]];
}int main()
{int n;scanf("%d",&n);vector<vector<int>> tree(n+1,vector<int>(n,0));vector<int> w(n+1,0);for(int i=1;i<=n;i++){scanf("%d",&w[i]);}for(int i=0,a=0,b=0;i<n-1;i++){scanf("%d%d",&a,&b);tree[a][b]=1;
//		tree[a].push_back(b);}int sum=0,num=0; for(int i=1;i<=n;i++)/*从叶到根*/{sum=0,num=0;for(int j=1;j<=n;j++)/*从小到大寻找父节点*/{if(tree[j][i]==1)/*j为父节点*/{for(int k=1;k<=n;k++)/*遍历子节点*/{if(tree[j][k]==1){num++;tree[j][k]=2;sum+=w[k];}}if(sum>w[j]){w[j]=dfs(j,num,tree,w,n);}}}}cout<<w[1];return 0;
}

        因为我连测试点都没过,不出所料一片红:

        与上一题一样,暴力的解法应该只有AC和TLE,所以我又开始找错误之旅。 

    经过我的不断修改,我决定放弃上边这套烂代码,用邻接表代替邻接矩阵来写(邻接矩阵太麻烦),并且通过dfs搜索,来实现树的遍历,但依旧采用01背包的思路去做:代码如下:

#include <iostream>
#include <vector>
using namespace std;const int N = 1010;
int n, w[N];
vector<int> g[N];/*邻接表*/
int f[N][N]={0};// 深度优先搜索
void dfs(int u) {// 叶节点情况(g[u].empty()为NULL 输出1),叶节点的材料产出就是其权值if (g[u].empty()) {f[u][w[u]] = w[u];return;}// 对当前节点的每个子节点进行DFSfor (int i = 0; i < g[u].size(); i++) {int v = g[u][i];dfs(v);}for (int i = 1; i <= w[u]; i++)/*初始化*/{if(w[g[u][0]]<=i)f[u][i]=w[g[u][0]];}  for (int i = 1; i < g[u].size(); i++)/*物品*/{for (int j=w[u];j>=0;j--)/*背包*/f[u][j]=max(f[u][j],f[u][j-w[g[u][i]]]+w[g[u][i]]);}
}int main() {cin >> n;for (int i = 1; i <= n; i++) {cin >> w[i];}for (int i = 1; i < n; i++) {/*邻接表*/int a, b;cin >> a >> b;g[a].push_back(b);}dfs(1);cout << f[1][w[1]] << endl;return 0;
}

        通过的测试点如下:

        比上次多了两个,呃,但是我想的应该是除了AC就是TLE,这显然不满足。最后我发现了这个致命的问题,虽然用了动态规划,但动态规划只造成了局部最优(每一个父节点与它的子节点),局部最优不代表全局最优,现在应该算是贪心,所以应该还有一层动态规划。

        这里又发现一个问题!!我们来看输入,“每行两个整数表示树上的一条边连接的两个结点”假设为a,b;这里并没有说输入的两个数顺序是a是b的入度,或者a是b的出度。题目中也没有明确说明哪个是根节点,但样例中是1,所以我们只能推测小数在前,即为根节点和父节点,故修改输入代码:

    for (int i = 1; i < n; i++) {/*邻接表*/int a, b;cin >> a >> b;if(a>b) swap(a,b);g[a].push_back(b);}

        多过了一个测试点: 

        这两层动态规划是难到我了,我感觉应该是绕到坑里了,所以我就直接去看洛谷题解了。 关键词:树形DP、分组背包。

试题 H: 装修报价(20分)

        洛谷链接:P12137 [蓝桥杯 2025 省 B] 装修报价 - 洛谷

【输入格式】

第一行输入一个整数 N ,表示装修相关费用的项数。
第二行输入 N 个非负整数 A 1 , A 2 , . . . , A N ,表示各项费用。

【输出格式】

输出一个整数,表示所有可能的总和对 10 9 + 7 取余后的结果。

 【样例输入】

3
0 2 5

【样例输出】

11

【样例说明】

对于输入样例中的三个数 A = [0 , 2 , 5] ,所有可能的运算符组合共有 9 种。
计算结果如下:
0 2 5 = 7 ,
0 2 + 5 = 7 ,
0 2 5 = 3 ,
0 + 2 5 = 7 ,
0 + 2 + 5 = 7 ,
0 + 2 5 = 3 ,
0 2 5 = 7 ,
0 2 + 5 = 3 ,
0 2 5 = 7 .
所有结果的总和为:
7 + 7 + ( 3) + 7 + 7 + ( 3) + ( 7) + 3 + ( 7) = 11
11 10 9 + 7 取余后的值依然为 11 ,因此,输出结果为 11

【评测用例规模与约定】

对于 30 % 的评测用例, 1 N 13 0 A_i   10^3
对于 60 % 的评测用例, 1 N 10 3 0 ≤ A_i 10^5
对于 100 % 的评测用例, 1 N 10 5 0 ≤ A_i10^9  。

        通过dfs搜索,应该是能得到部分分的,但是考试时我没写这道题,因为我忘记了异或是什么。。。。dfs暴力我就不演示了,懒得写了。

        AC题解,也是先找规律。。。具体可以看洛谷题解。正和负相互抵消了。

吐槽

        感觉蓝桥杯的题更适合高中、初中的同学来写,今年都是些规律题,与CSP、CCSP差远了。而且今年的提交方式已经和计挑赛有的一拼了,只能提交,不能调试(模拟赛还能知道答案是否正确,到正式比赛却又换了)。

相关文章:

  • 沙箱逃逸(Python沙盒逃逸深度解析)
  • 7.进程概念(三)
  • 01_微服务常见问题
  • k8s术语pod
  • 解决vue3 路由query传参刷新后数据丢失的问题
  • Webug4.0通关笔记04- 第6关宽字节注入
  • FPGA中级项目7———TFT显示与驱动
  • gitmodule怎么维护
  • LeetCode:55.跳跃游戏——局部最优并非全局最优!
  • 如何个人HA服务器地址和长期密钥
  • 分享一个移动端项目模板:React-Umi4-mobile
  • 从厨房到云端:从预制菜到云原生
  • 浏览器打印日志方法与技巧
  • java连接redis服务器
  • vue 优化策略,大白话版本
  • Spring Security 的 CSRF 防护机制
  • [Android]导航栏中插入电源菜单
  • 【深度学习新浪潮】ISP芯片算法技术简介及关键技术分析
  • 强化学习之基于无模型的算法之蒙特卡洛方法
  • 【题解-Acwing】871. 约数之和
  • 中国贸促会:有近50%的外贸企业表示将减少对美业务
  • 经济日报:多平台告别“仅退款”,规则调整有何影响
  • 核电开闸!国常会核准10台新机组,拉动超2000亿投资,新项目花落谁家?
  • 银川市长信箱被指乱回复:问诗词大会、答工程欠款,官方称工作失误
  • QFII一季度现身超300家公司:持有南京银行市值最高,5家青睐立航科技
  • 106岁东江纵队老战士、四川省侨联名誉主席邱林逝世