2021蓝桥杯第八次训练赛

A : 组队

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

作为篮球队教练,你需要从以下名单中选出\(1\)号位至\(5\)号位各一名球员,
组成球队的首发阵容。每位球员担任\(1\)号位至\(5\)号位时的评分如下表所示。
请你计算首发阵容\(1\)号位至\(5\)号位的评分之和最大可能是多少?

编号 1号位 2号位 3号位 4号位 5号位
1    97    90    0     0     0
2    92    85    96    0     0
3    0     0     0     0     93
4    0     0     0     80    86
5    89    83    97    0     0
6    82    86    0     0     0
7    0     0     0     87    90
8    0     97    96    0     0
9    0     0     89    0     0
10   95    99    0     0     0
11   0     0     96    97    0
12   0     0     0     93    98
13   94    91    0     0     0
14   0     83    87    0     0
15   0     0     98    97    98
16   0     0     0     93    86
17   98    83    99    98    81
18   93    87    92    96    98
19   0     0     0     89    92
20   0     99    96    95    81

More Info

需要提交的是一个整数,不要填写任何多余内容。



Solution

由于数据较小,可以直接使用DFS进行暴力算解。

  • TAG:DFS深度优先搜索

std.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ans,point[21][6];
bool vis[21];
void dfs(int x,int res){
	if(x>5){
		ans=max(ans,res);
		return;
	}
	for(int i=1;i<=20;++i)
		if(!vis[i]){
			vis[i]=1;
			dfs(x+1,res+point[i][x]);
			vis[i]=0;
		}
}
int main(){
	for(int x,i=1;i<=20;++i){
		scanf("%d",&x);
		for(int j=1;j<=5;++j){
			scanf("%d",&point[i][j]);
		}
	}
	dfs(1,0);
	printf("%d",ans);
//	puts("490"); 答案为490,将上面数据带入即可
	return 0;
}






B : 年号字串

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

小明用字母\(A\)对应数字\(1\)\(B\)对应\(2\),以此类推,用\(Z\)对应\(26\)
对于\(27\)以上的数字,小明用两位或更长位的字符串来对应,
例如\(AA\)对应\(27\)\(AB\)对应\(28\)\(AZ\)对应\(52\)\(LQ\)对应\(329\)
请问\(2019\)对应的字符串是什么?


More Info

这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个大写英文字符串。
在提交答案时只填写这个字符串,注意全部大写。
填写多余的内容将无法得分。
printf("");



PZ's Solution

1.显然,此为\(26\)进制转换的题目,关键在于\(Z\)实际上需要代之\(0\)

2.因为此进制转化非从\(0\)开始,但进制转换需要从\(0\)开始;

  • TAG:数学;进制转化

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ans[100];
int main(){
	int x=2019;
	//scanf("%d",&x);
	while(x){
		ans[++ans[0]]=x%26;
		if(x%26==0) x-=26;
		x/=26;
	}
	for(int i=ans[0];i;--i) printf("%c",ans[i]==0 ? 'Z' : ans[i]+'A'-1);
	return 0;
}






C : 数列求值

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

给定数列\(1, 1, 1, 3, 5, 9, 17, …,\)从第\(4\)项开始,每项都是前\(3\)项的和。
求第\(20190324\)项的最后\(4\)位数字。


More Info

需要提交的是一个整数,不要填写任何多余内容。



PZ's Solution

数据本身并不大,递推即可;

  • TAG:递推

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int nowf,f[4];
int main(){
	f[1]=1,f[2]=1,f[3]=1;
	for(int i=4;i<=20190324;++i){
		nowf=(f[1]+f[2]+f[3])%10000;
		f[1]=f[2];
		f[2]=f[3];
		f[3]=nowf;
	}
	printf("%d",nowf);
	return 0;
}






D : 数的分解

Time Limit:2.000 Sec Memory Limit:128 MiB


Description

\(2019\)分解成\(3\)个各不相同的正整数之和,并且要求每个正整数都不包含数字\(2\)\(4\)
一共有多少种不同的分解方法?
注意交换\(3\)个整数的顺序被视为同一种方法,
例如\(1000+1001+18\)\(1001+1000+18\)被视为同一种。


More Info

需要提交的是一个整数,不要填写任何多余内容。



PZ's Solution

1.注意 需要分解为各不相同的正整数之和;

2.可以使用三个变量i,j,k,严格控制i>j>k然后遍历判断即可;

  • TAG:遍历

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ans;
bool check(int x){
	while(x){
		int res=x%10;
		if(res==2||res==4) return 0;
		x/=10;
	}
	return 1;
}
int main(){
	for(int i=2017;i;--i)
		for(int j=i-1;j;--j){
			int k=2019-i-j;
			if(k<=0||k>=j) continue;
			if(check(i)&&check(j)&&check(k)){
				//cout<<i<<" "<<j<<" "<<k<<endl;
				++ans;
			}
		}
	printf("%d",ans);
	return 0;
}






E : 迷宫

Time Limit:2.000 Sec Memory Limit:128 MiB


Description

下图给出了一个迷宫的平面图,其中标记为1的为障碍,标记为0的为可以通行的地方。

010000
000100
001001
110000

迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR的顺序通过迷宫,一共\(10\)步。其中D、U、L、R分别表示向下、向上、向左、向右走。

对于下面这个更复杂的迷宫(\(30\)\(50\)列),请找出一种通过迷宫的方式,其使用的步数最少,
在步数最少的前提下,请找出字典序最小的一个作为答案。请注意在字典序中D<L<R<U

01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

More Info

本题的结果为一个字符串,包含四种字母D、U、L、R
在提交答案时只填写这个字符串,填写多余的内容将无法得分。



PZ's Solution

直接使用BFS,注意字典序即可;

  • TAG:BFS广度优先搜索

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
char mp[35][55];
string ans[35][55];
int fx[]={1,0,0,-1};
int fy[]={0,-1,1,0};
string F="DLRU";
queue<int>qx,qy;
bool vis[35][55];
void bfs(){
	qx.push(1); qy.push(1); vis[1][1]=1;
	while(!qx.empty()){
		int x=qx.front(),y=qy.front(); qx.pop(); qy.pop();
		for(int i=0;i<4;++i){
			int nx=x+fx[i],ny=y+fy[i];
			if(1<=nx&&nx<=30&&1<=ny&&ny<=50&&!vis[nx][ny]&&mp[nx][ny]=='0'){
				ans[nx][ny]=ans[x][y]+F[i];
				if(nx==30&&ny==50) return;
				qx.push(nx); qy.push(ny); vis[nx][ny]=1;
			}
		}
	}
}
int main(){
	for(int i=1;i<=30;++i)
		for(int j=1;j<=50;++j)
			cin>>mp[i][j];
	bfs(); 
	cout<<ans[30][50];
//	puts("DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR"); 此为答案,代入数据即可得到;
	return 0;
}






F : 特别数的和

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

小明对数位中含有\(2、0、1、9\)的数字很感兴趣(不包括前导\(0\)),
\(1\)\(40\)中这样的数包括\(1、2、9、10\)\(32、39\)\(40\),共\(28\)个,他们的和是\(574\)
请问,在\(1\)\(n\)中,所有这样的数的和是多少?


Input

输入一行包含一个整数\(n\)


Output

输出一行包含一个整数,表示满足条件的数的和。


Sample Input

40

Sample Output

574

More Info

对于\(20\%\)的评测用例,\(1 \leqslant n \leqslant 10\)
对于\(50\%\)的评测用例,\(1 \leqslant n \leqslant 100\)
对于\(80\%\)的评测用例,\(1 \leqslant n \leqslant 1000\)
对于所有评测用例,\(1 \leqslant n \leqslant 10000\)



PZ's Solution

参考D题,直接遍历即可;

  • TAG:遍历

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,ans;
bool check(int x){
	while(x){
		int res=x%10;
		if(res==2||res==0||res==1||res==9) return 1;
		x/=10;
	}
	return 0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		if(check(i)) ans+=i;
	printf("%d",ans);
	return 0;
}






G : 完全二叉树的权值

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

给定一棵包含\(N\)个节点的完全二叉树,树上每个节点都有一个权值,按从上到下、从左到右的顺序依次是\(A_1, A_2, ... A_N\),如下图所示:

现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点权值之和最大?
如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是\(1\)


Input

第一行包含一个整数\(N\)
第二行包含\(N\)个整数\(A_1, A_2, ... A_N\)


Output

输出一个整数代表答案。


Sample Input

7
1 6 5 4 3 2 1

Sample Output

2

More Info

对于所有评测用例,\(1 \leqslant N \leqslant 100000,-100000 \leqslant Ai \leqslant 100000\)



PZ's Solution

1.考虑对于满二叉树,第\(x\)层的结点数为\(2^{x-1}\),直接遍历累加即可;

2.但本题为完全二叉树,且有两个坑点:

1).结点值可能为负数,所以一定要累加完再进行比较;
2).完全二叉树最后一层可能不满\(2^{x-1}\),要进行特判;

  • TAG:遍历

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
int pre2,nowpre,nowdep,maxdep,n,maxans=-INT_MAX,ans;
int main(){
	pre2=nowpre=nowdep=1;
	//pre2:当前深度的结点数
	//nowpre:因为上值需要记录,此值作为pre2的复制,进行加减操作
	//nowdep:当前深度
	scanf("%d",&n);
	for(int x,i=1;i<=n;++i){
		scanf("%d",&x);
		ans+=x;
		--nowpre;
		if(nowpre==0||i==n){
		//要确保当前层全部累加完毕,或者最后一层结束
			if(maxans<ans){
				maxans=ans;
				maxdep=nowdep;
			}
			pre2*=2;
			nowpre=pre2;
			++nowdep;
			ans=0;
		}
	}
	printf("%d",maxdep);
	return 0;
}






H : 等差数列

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中\(N\)个整数。
现在给出这\(N\)个整数,小明想知道包含这\(N\)个整数的最短的等差数列有几项?


Input

输入的第一行包含一个整数\(N\)
第二行包含\(N\)个整数\(A_1, A_2,... A_N\)。(注意\(A_1\)\(A_N\)并不一定是按等差数列中的顺序给出)


Output

输出一个整数表示答案。


Sample Input

5
2 6 4 10 20

Sample Output

10

More Info

包含\(2、6、4、10、20\)的最短的等差数列是\(2、4、6、8、10、12、14、16、18、20\)
对于所有评测用例,\(2 \leqslant N \leqslant 100000,0 \leqslant A_i \leqslant 1000000000\)



PZ's Solution

1.本题需要 使公差\(d\)尽可能大;

2.考虑到给出的\(A_i\),排序后 相邻两项的差值 都可能对公差\(d\)有贡献;

3.举例差值有\(\{6,9\}\),则公差很有可能为\(d=3\),可以使公差\(d\)最大,考虑最大公约数;

4.对所有差值使用最大公约数,即可找到最大的公差,且最大值与最小值必为等差数列左右两端;

  • TAG:数论;GCD最大公约数

PZ.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[100005],d;
int gcd(int x,int y){ return y==0 ? x : gcd(y,x%y); }
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	d=a[2]-a[1];
	for(int i=2;i<n;++i) d=gcd(a[i+1]-a[i],d);
	printf("%d",(a[n]-a[1])/d+1);
	return 0;
}






I : 后缀表达式

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

给定\(N\)个加号、\(M\)个减号以及\(N + M + 1\)个整数\(A_1, A_2, · · · , A_{N+M+1}\),小明想知道在所有由这\(N\)个加号、\(M\)个减号以及\(N + M + 1\)个整数凑出的合法的后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用1 2 3 + -,则 2 3 + 1 - 这个后缀表达式结果是4,是最大的。


Input

第一行包含两个整数\(N\)\(M\)
第二行包含\(N + M + 1\)个整数\(A_1, A_2, · · · , A_{N+M+1}\)


Output

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


Sample Input

1 1
1 2 3

Sample Output

4



Solution

思路来自TRTTG的2019蓝桥杯初赛 后缀表达式

1.题目的后缀表达式,实际上是要求出\(N\)个加号,\(M\)个减号,\(N+M+1\)个数组成的 带括号 的 式子的 最大值;

2.对于理想情况,有\(N+1\)个正数,\(M\)个负数,则答案即为\(\sum_{i=1}^{N+M+1}|A_i|\)(即所有数的绝对值之和);

3.如果没有减号,显然结果为\(\sum_{i=1}^{N+M+1}A_i\)(即所有值相加的和);


4.如果 正数个数\(>\)负数个数,设\(X、Y、Z\)为 若干正数和,\(x\)为若干负数和:

1).如果有负数,则可以构成\(X-(x-Y)=X-x+Y\),结果仍为\(\sum_{i=1}^{N+M+1}|A_i|\)
2).如果没有负数,则可以构成\(X-(Y-Z)=X-Y+Z\),则需要使\(Y\)最小,结果为\(\sum_{i=1}^{N+M+1}|A_i|-2*A_{min}\)


5.如果 负数个数\(>\)正数个数,设\(X\)为 若干正数和,\(x、y、z\)为若干负数和:

1).如果有正数,则可以构成\(X-(x+y)=X-x-y\),结果仍为\(\sum_{i=1}^{N+M+1}|A_i|\)
2).如果没有正数,则可以构成\(x-(y+z)=x-y-z\),则需要使\(x\)最大,结果为\(\sum_{i=1}^{N+M+1}|A_i|+2*A_{max}\)

  • TAG:思维

std.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#include<cmath>
using namespace std;
int n,m,a[10000005],sum,abssum,Y,y,mina=INT_MAX,maxa=-INT_MAX;
int main(){
	scanf("%d %d",&n,&m);
	for(int x,i=1;i<=n+m+1;++i){
		scanf("%d",&x);
		sum+=x; abssum+=abs(x);
		if(x>=0) ++Y;
		else ++y;
		mina=min(mina,x);
		maxa=max(maxa,x);
	}
	if(m==0) printf("%d",sum);
	else if(Y>y){
		if(y) printf("%d",abssum);
		else printf("%d",abssum-2*mina);
	} 
	else if(Y<y){
		if(Y) printf("%d",abssum);
		else printf("%d",abssum+2*maxa);
	}
	return 0;
}






J : 灵能传输

Time Limit:1.000 Sec Memory Limit:256 MiB


Description

在游戏《星际争霸 II》中,高阶圣堂武士作为星灵的重要\(AOE\)单位,在游戏的中后期发挥着重要的作用,其技能”灵能风暴“可以消耗大量的灵能对一片区域内的敌军造成毁灭性的伤害。

经常用于对抗人类的生化部队和虫族的刺蛇飞龙等低血量单位。

你控制着\(n\)名高阶圣堂武士,方便起见标为\(1,2,⋅⋅⋅,n\)

每名高阶圣堂武士需要一定的灵能来战斗,每个人有一个灵能值\(a_i\)表示其拥有的灵能的多少(\(a_i\)非负表示这名高阶圣堂武士比在最佳状态下多余了\(a_i\)点灵能,\(a_i\)为负则表示这名高阶圣堂武士还需要\(−ai\)点灵能才能到达最佳战斗状态)。

现在系统赋予了你的高阶圣堂武士一个能力,传递灵能,每次你可以选择一个\(i \in [2,n−1]\),若\(ai \geqslant 0\)则其两旁的高阶圣堂武士,也就是\(i−1、i+1\)这两名高阶圣堂武士会从\(i\)这名高阶圣堂武士这里各抽取\(a_i\)点灵能;若\(a_i<0\)则其两旁的高阶圣堂武士,也就是\(i−1,i+1\)这两名高阶圣堂武士会给\(i\)这名高阶圣堂武士\(−a_i\)点灵能。

形式化来讲就是\(a_{i−1}+=a_i,a_{i+1}+=a_i,a_i−=2a_i\)

灵能是非常高效的作战工具,同时也非常危险且不稳定,一位高阶圣堂武士拥有的灵能过多或者过少都不好,定义一组高阶圣堂武士的不稳定度为\(max^{n}_{i=1}|a_i|\),请你通过不限次数的传递灵能操作使得你控制的这一组高阶圣堂武士的不稳定度最小。


Input

本题包含多组询问。输入的第一行包含一个正整数\(T\)表示询问组数。

接下来依次输入每一组询问。

每组询问的第一行包含一个正整数\(n\),表示高阶圣堂武士的数量。

接下来一行包含\(n\)个数\(a_1,a_2,⋅⋅⋅,a_n\)


Output

输出\(T\)行。


Sample Input

3
3
5 -2 3
4
0 0 0 0
3
1 2 3

Sample Output

3
0
3



Solution

思路来自Belous的蓝桥杯:2019年第十届蓝桥杯省赛B组试题J — 灵能传输

1.设到第\(i\)个高阶圣堂武士的灵能的前缀和为\(S_i\),则题目转换为求不限次数的传递灵能操作后,\(max(|S_i-S_{i-1}|)\)

2.对于当前第\(i\)个高阶圣堂武士\(a_i\),如果进行传递灵能操作,有:

\(a_{i-1}=a_{i-1}+a_i,a_i=a_i-2*a_i=-a_i,a_{i+1}=a_{i+1}+a_i\)

\(S_{i-1}=原S_i,S_i=原S_{i-1},S_{i+1}=S_{i+1}\)

可以发现,传递灵能操作可以使\(S_i\)\(S_{i-1}\)的值互换;

3.但这里有一个坑点,即\(S_0\)\(S_n\)实际上不能被交换,所以不能依靠直接排序寻找\(max(S_i-S_{i-1})\)

4.假设\(S_0<S_n\),实际上需要将\(S_1 \sim S_{n-1}\)排序,使得\(max(|S_i-S_{i-1}|)\)最小;

5.可以考虑将\(S\)整个排序,然后下图所示红线顺序重新进行排序,保证\(S_0\)\(S_n\)固定;

但是特别的,对于蓝线所在区域,不能选取两次,所以对于蓝线部分,使用隔一个数取一个的做法,确保\(max(|S_i-S_{i-1}|)\)最小;

  • TAG:贪心

std.cpp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
ll T,n,ans,s[300010],s0,sn,s0tmp,sntmp,S[300010];
bool vis[300010];
int main(){
	scanf("%lld",&T);
	while(T--){
		ans=s[0]=0;
		memset(vis,0,sizeof(vis));
		scanf("%lld",&n);
		for(int i=1;i<=n;++i){
			scanf("%lld",&s[i]);
			s[i]+=s[i-1];
		}
		s0=0,sn=s[n];
		if(s0>sn) swap(s0,sn);
		sort(s,s+1+n);
		for(int i=0;i<=n;++i){
			if(s0==s[i]) s0tmp=i;
			if(sn==s[i]) sntmp=i;
		}
		int l=0,r=n;
		for(int i=s0tmp;i>=0;i-=2){
			S[l++]=s[i];
			vis[i]=1;
		}
		for(int i=sntmp;i<=n;i+=2){
			S[r--]=s[i];
			vis[i]=1;
		}
		for(int i=0;i<=n;++i)
			if(!vis[i]) S[l++]=s[i];
		for(int i=1;i<=n;++i)
			ans=max(ans,abs(S[i]-S[i-1]));
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-02-22 03:56  PotremZ  阅读(366)  评论(0编辑  收藏  举报