做之前的真题就可以发现,蓝桥杯特别喜欢出找规律的题,但是我还是低估了官方的执念。本博客用于记录第一次蓝桥的过程,代码写的很烂,洛谷已经有的题解,这里不再赘述,只说自己遇到的问题。用于以后回顾和查找资料,毕竟自己总结的印象更深。
本博客,没有多少借鉴价值,基本全是暴力破解和吐槽。
试题 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可以,但是又不知道如何判断和
的乘积不得超过 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”,发现次数等于
只需要再根据规律,算出结果即可。
#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 ,表示需要判断是否可分解的正整数序列。
【输出格式】
输出一个整数,表示给定数据中可分解的正整数的数量。
【样例输入】
33 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 ≤≤ 100 。
对于 100 % 的评测用例, 1 ≤ N ≤, 1 ≤
≤
。
这道题再知乎上也被很多人吐槽了,除了1全部可以。我的思路是从找规律开始,找他的样例:
0+1+2 = 3
1+2+3 = 6
2+3+4 = 9
会发现长度为三的序列可以输出所有三的倍数,所以只要是3的倍数都可以分解。
但是要注意题目条件是“序列长度至少为3”,那如果长度为4,就是4的倍数也全部可分解,长度为5,就是5的倍数可以分解......这样下去,岂不是所有正整数都可以。恰好 “1 ≤ ≤
”。
而且可以有负数,那么...:
-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 次调整后金矿、银矿和铜矿的产值,用空格分隔。
【样例输入】
210 20 30 15 5 5 3
【样例输出】
25 20 155 5 5
【评测用例规模与约定】
对于 30 % 的评测用例, 1 ≤ T ≤ 100 , 1 ≤ A , B , C , K ≤。
对于 100 % 的评测用例, 1 ≤ T ≤ 10 5 , 1 ≤ A , B , C , K ≤。
看到这道题,思路不难,那就可能会出现超时问题,那就代表可能有规律,但是我没看出来,所以就先写出暴力解法看看:
#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;
}
运行例题通过了,那么就稍微改一下样例试试,加大看看是否超时:
210 20 30 505 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 21 5 2 4
【样例输出】
3
【评测用例规模与约定】
对于 40 % 的评测用例, 2 ≤ M ≤ N ≤ 10 3 , 1 ≤ A i ≤。
对于 100 % 的评测用例, 2 ≤ M ≤ N ≤ 10 5 , 1 ≤ A i ≤。
这道题思路洛谷上题解也已经讲的很清楚(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 行,每行两个整数表示树上的一条边连接的两个结点。
【输出格式】
输出共一行,一个整数代表答案。
【样例输入】
99 7 3 7 1 6 2 2 71 21 32 42 52 66 76 86 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 取余后的结果。
【样例输入】
30 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) = 1111 对 10 9 + 7 取余后的值依然为 11 ,因此,输出结果为 11 。
【评测用例规模与约定】
对于 30 % 的评测用例, 1 ≤ N ≤ 13 , 0 ≤≤
。
对于 60 % 的评测用例, 1 ≤ N ≤ 10 3 , 0 ≤≤
。
对于 100 % 的评测用例, 1 ≤ N ≤ 10 5 , 0 ≤≤
。
通过dfs搜索,应该是能得到部分分的,但是考试时我没写这道题,因为我忘记了异或是什么。。。。dfs暴力我就不演示了,懒得写了。
AC题解,也是先找规律。。。具体可以看洛谷题解。正和负相互抵消了。
吐槽
感觉蓝桥杯的题更适合高中、初中的同学来写,今年都是些规律题,与CSP、CCSP差远了。而且今年的提交方式已经和计挑赛有的一拼了,只能提交,不能调试(模拟赛还能知道答案是否正确,到正式比赛却又换了)。