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

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

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

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

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

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

豆包代码:

cpp 复制代码
#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] 客流量上限 - 洛谷):

cpp 复制代码
#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",发现次数等于

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

cpp 复制代码
#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 ≤ ≤ 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"。

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

cpp 复制代码
#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;
}

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

cpp 复制代码
#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 行,每行包含四个整数 ABCK ,分别表示金矿、银矿和
铜矿的初始产值,以及需要执行的调整次数。

【输出格式】

对于每个测试用例,输出一行,包含三个整数,表示经过 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
对于 100 % 的评测用例, 1 ≤ T ≤ 10 5 , 1 ≤ A , B , C , K

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

cpp 复制代码
#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不同,后边运算全相同,再换几个数也发现同样的规律。

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

cpp 复制代码
#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] 画展布置 - 洛谷

【输入格式】

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

【输出格式】

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

【样例输入】

4 2
1 5 2 4

【样例输出】

3

【评测用例规模与约定】

对于 40 % 的评测用例, 2 ≤ MN ≤ 10 3 , 1 ≤ A i
对于 100 % 的评测用例, 2 ≤ MN ≤ 10 5 , 1 ≤ A i

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

cpp 复制代码
#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"类型,便全部通过了:

cpp 复制代码
#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。:

代码如下:

cpp 复制代码
#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的遍历方向上,我只遍历了"左","上"连个方向,这种情况只适用于,起点在第一行的情况,但如果出现在第二行,便永远不能到达第一行了,所以我又加了一个向上的方向。

cpp 复制代码
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背包构造的,代码如下:

cpp 复制代码
#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];
			else
				data[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背包的思路去做:代码如下:

cpp 复制代码
#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;
    }

    // 对当前节点的每个子节点进行DFS
    for (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,所以我们只能推测小数在前,即为根节点和父节点,故修改输入代码:**

cpp 复制代码
    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 ≤
对于 60 % 的评测用例, 1 ≤ N ≤ 10 3 , 0 ≤
对于 100 % 的评测用例, 1 ≤ N ≤ 10 5 , 0 ≤

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

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

吐槽

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

相关推荐
绒绒毛毛雨9 分钟前
将infinigen功能集成到UE5--在ue里面写插件(python和c++)
c++·python·ue5
海码00718 分钟前
【Hot 100】 148. 排序链表
数据结构·c++·链表·排序算法·hot100
余弦的倒数33 分钟前
C++的vector中emplace_back() 与 push_back() 的区别
开发语言·c++
鱼糕权八郎 -33 分钟前
LeetCode392_判断子序列
c++·leetcode
到底怎么取名字不会重复43 分钟前
Day16(贪心算法)——LeetCode45.跳跃游戏II&763.划分字母区间
c++·算法·leetcode·游戏·贪心算法
染指11101 小时前
18.第二阶段x64游戏实战-MFC列表框
汇编·c++·windows·游戏·游戏逆向·x64dbg
enyp801 小时前
Qt文本文件读写方法详解
开发语言·c++·算法
OG one.Z3 小时前
文件读取操作
c++·学习·文件读取
彷徨而立3 小时前
【C++】频繁分配和释放会产生内存碎片
开发语言·c++