第十届蓝桥杯大赛软件类省赛 C++ B组 试题+题解

写在前面,这届蓝桥杯,考了2道贪心,1道bfs,1道数论,剩下的都是模拟题,除了最后一题之外,其他题还是都很简单的

试题 A: 组队

在这里插入图片描述
在这里插入图片描述

题解: 这个题其实可以手推,编程实现的话就是dfs暴力枚举所有情况,最后求出评分最大即可。

C++ 代码:

#include <iostream>
#include <algorithm> 

using namespace std;
const int N = 25;
int team[N][N];
bool st[N];
int res = 0;

void dfs(int u,int sum)
{
	if(u == 6)
	{
		res = max(res, sum);
		return;
	}
	for(int i = 0; i < 20; i ++ )
	{
		if(!st[i])
		{
			st[i] = true;
			dfs(u + 1, sum + team[i][u]);
			st[i] = false;
		}
	}
}

int main()
{
	freopen("team.txt", "r", stdin);
	
	for(int i = 0; i < 20; i ++ )
		for(int j = 0; j < 6; j ++ )
			cin >> team[i][j];
	dfs(1, 0);
	
	cout << res << endl;
	return 0;
}
//490

试题 B:年号字串

在这里插入图片描述
题解:
直接考虑成26进制就可以,注意最后要reverse一下

#include <iostream>
#include <algorithm>
using namespace std;
 
int main()
{
	string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	string res;
	int x = 2019;
	while(x)
	{
		int t = x % 26;
		x /= 26;
		res += str[t - 1];
	}
	reverse(res.begin(), res.end());
	cout << res << endl;
	return 0;
}
//BYQ

试题 C:数列求值

在这里插入图片描述
题解: 很简单的一个dp操作,但是要注意的是题目要求输出后四位,所以需要%10000

C++ 代码:

#include <iostream>
#include <algorithm>

using namespace std;
const int N = 20200000;
long long dp[N]; 

void dfs()
{
	for(int i = 3; i <= 20190324; i ++ )
		dp[i] = (dp[i - 1] + dp[i - 2] + dp[i - 3]) % 10000;
}

int main()
{
	dp[0] = 1;
	dp[2] = 1;
	dp[1] = 1;
	
	dfs();
	
	cout << dp[20190323] << endl;
	
	return 0;
}
// 4659

试题 D:数的分解

在这里插入图片描述
题解: 直接模拟就可以,注意i,j,k的顺序,我们直接人为规定i < j < k来确保我们的答案的唯一性

C++ 代码:

#include <iostream>
#include <algorithm>

using namespace std;


bool check(int x)
{
	while(x)
	{
		int t = x % 10;
		x /= 10;
		if(t == 2 || t == 4) return false; 
	}
	return true;
}

int main()
{
	int res = 0;
	for(int i = 1; i < 2019; i ++ )
		if(check(i))
		{
			for(int j = i + 1; j < 2019; j ++ )
				if(check(j))
				{
					int k = 2019 - i - j;
					if(check(k) && k > j) res ++;	
				}	
		} 
	cout << res << endl; 
	return 0;
}
// 40785 

试题 E:迷宫

在这里插入图片描述
在这里插入图片描述

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

题解 bfs + 路径记忆

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int n = 30, m = 50;

int g[n][m];
struct Point 
{
	int x, y;
	string path;
}P; 

char op[4] = {'D', 'L', 'R', 'U'};

int dx[4] = {1, 0, 0, -1}, dy[4] = {0, -1, 1, 0};

void bfs()
{
	queue<Point> q;
	Point p({0,0});
	q.push(p);
	p.path = "";
	
	while(q.size())
	{
		Point pt = q.front();
		q.pop();
		
		if(pt.x == 29 && pt.y == 49) 
		{
			cout << pt.path << endl;
			return;	
		} 
		for(int i = 0; i < 4; i ++ )
		{
			int tx = pt.x + dx[i], ty = pt.y + dy[i];
			if(tx >= 0 && tx < 30 && ty >= 0 && ty < 50 && !g[tx][ty]) 
			{
				g[tx][ty] = 1;
				Point pp({tx, ty});
				pp.path = pt.path + op[i];
				q.push(pp);
			}
		}
	}
}

int main()
{
	freopen("migong.txt", "r", stdin);
	
	string t;
	for(int i = 0; i < n; i ++ )
	{
		cin >> t;
		for(int j = 0; j < m; j ++ )
		{
			g[i][j] = t[j] - '0';
		}
	}
	bfs();
	return 0;	
}
答案:
DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

试题 F:特别数的和

在这里插入图片描述
题解: 模拟就好了

C++ 代码:

#include <iostream>
#include <algorithm> 

using namespace std;

bool check(int x)
{
	while(x)
	{
		int t = x % 10;
		if(t == 0 || t == 1 || t == 2 || t == 9) return true;
		x /= 10; 
	} 
	return false;
}

int main()
{
	int n;
	cin >> n;
	
	long long res = 0;
	for(int i = 1; i <= n; i ++ )
	{
		if(check(i)) res += i;
	}
	
	cout << res << endl;
	return 0;
}

试题 G:完全二叉树的权值

在这里插入图片描述
题解:
完全二叉树的权值
双指针
每一段的长度是 i + 2 ( d − 1 ) i+2^{(d-1)} i+2(d1), 并且j不能越界
具有n个结点的完全二叉树的深度 l o g 2 k + 1 log_{2}k +1 log2k+1

C++ 代码:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int N = 100010;

int n;
int tree[N];

int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++ ) scanf("%d", &tree[i]);
	
	long long maxx = -1e6;
	int depth = 0;
	
	for(int i = 1, d = 1; i <= n; i *= 2, d ++ )
	{
		long long t = 0;
		for(int j = i; j < i + (1 << (d - 1)) && j <= n; j ++ )
			t += tree[j];
		
		if(t > maxx)
		{
			maxx = t;
			depth = d;
		}
	} 
	cout << depth << endl;
	return 0;
}

试题 H:等差数列

在这里插入图片描述
在这里插入图片描述
题解:
每一项与第一项的差一定是d的倍数
当d != 0 时, (a末 - a初) / d + 1 ---- 让公差d最大即可
当d == 0 时,答案为 n

c++ 代码:

#include <iostream>
#include <algorithm>
#include <climits>

using namespace std;
const int N = 100010;

int n;
int a[N];

int main()
{
	cin >> n;
	for(int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
	
	sort(a, a + n);
	
	int mgcd = 0;
	
	for(int i = 1; i < n; i ++ ) 
	{
		mgcd = __gcd(mgcd, a[i] - a[0]);
	}
	
	if(mgcd == 0)
	{
		cout << n << endl;
		return 0;
	}
	
	cout << (a[n - 1] - a[0]) / mgcd + 1 << endl; 
	return 0;
}

试题 I:后缀表达式

在这里插入图片描述
题解:
给题意翻译翻译,其实就是有n个加号,m个减号,n+m+1个数,可以加括号,问组成表达式的最大值。
特殊情况:m=0,直接输出和
一般情况:把所有数排个序,最大的拿出来,放首项,把最小的数拿出来,给他一个减号,再套一个括号,那么现在还未完成的表达式长这样:

可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。

C++ 代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 200010;
typedef long long LL;

int n, m;
int a[N];

int main()
{
    scanf("%d%d", &n, &m);
    int k = n + m + 1; 
    for(int i = 0; i < k; i ++ ) scanf("%d", &a[i]);
    
    sort(a, a + k);
    
    LL res = 0;
    if(m <= 0) 
        for(int i = 0; i < k; i ++ ) res += a[i];
    else
    {
        res = a[k - 1] - a[0];
        for(int i = 1; i < k - 1; i ++ ) res += abs(a[i]);
    }
    
    
    cout << res << endl;
    return 0;
}

试题 J:灵能传输

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题解:
搞了好久这题,终于明白了,过程分的有点多,而且每一个过程都不是很容易想到,所以会觉得听完y总讲的后感觉根本没听懂,我是也是前后听了好几遍才明白的,然后把思路大致整理一下

搞明白这一题就一定要时刻铭记我们的最终目标是什么。
根据题意我们的最终目标就是使得每个战士的灵能的不稳定度最小,就是经过变化后,序列里的所有数离0最大值最小
可以考虑我们所要求的序列是 a i a_i ai 那么可以考虑用前缀和数组来表示
a i a_i ai 就是 s i − s i − 1 s_i - s_{i-1} sisi1
那么有了这个基础我们来看题目要求的数据变化,形式化来讲就是 a i − 1 + = a i , a i + 1 + = a i , a i − = 2 ⋅ a i a_{i−1} += a_i , a_{i+1} += a_i, a_i −= 2·a_i ai1+=ai,ai+1+=ai,ai=2ai
考虑 a a a 序列的变化对于 s s s 数组的影响,可以看出加了2个 a i a_i ai 同时减了2个 a i a_i ai那么对于 s i + 1 s_{i + 1} si+1 以及后面 s i + k s_{i+k} si+k来说都没有任何影响,看 s i − 1 s_{i - 1} si1 可以看出多加了一个 a i a_i ai 所以 s i − 1 s_{i-1} si1变为 s i s_i si s i s_i si
可以看出 a i − 1 a_{i-1} ai1 加了一个 a i a_i ai , a i a_i ai减了两个 a i a_i ai所以 s i s_i si 总共减了一个 a i a_i ai变为 s i − 1 s_{i-1} si1
过程①总结一下:

  • 考虑用前缀和来表示 a i a_i ai ,找出了经题目要求变化 s 前 s_{前} s s 后 s_{后} s的区别 这也是最难想到的一步

  • 这意味着除了s[0]s[n]以外$ 1 - n $ 的任何s[i]可以进行相互交互从而得到一个有序的序列,而a[i]=s[i]-s[i-1],也就意味着可以通过交换s[i]的方式得到灵能传输后最终结果

  • 根据题意我们要求的是 a i a_i ai 距 0 的最大值最小是多少, 转化为 s i − s i − 1 s_i - s_{i - 1} sisi1 最大值最小是多少

这样就引出了我们的我第二步,什么样的前缀和序列使得max(s[i] - s[i-1])最小,利用贪心的思路知:有序的前缀和序列使得所求最小

下面是这一思路的证明:一个序列一定存在一个最大值和一个最小值,如果不是有序序列的话,即最大值和最小值不在两边的时候,就会出现曲线,如图:
捕获.PNG

这样就会存在最大值和最小值刚好相邻,但是求其差值回为最大。其实分析到这里这个题的大体就结束了,但是狗就狗在
s0 和 sn 不能计算到排序中,因为a0 和 an 在两边导致s0 和 sn的 位置固定,我们无法修改这两个数的位置,那么就使得我们最终得到的si序列不是单调的

这就引出了我们的第③步
在一直s0和sn的前提下,怎样使得我们得到的序列满足我们的要求呢,要求是什么,这一点很重要,千万别忘,否则就很难理解这一步,回头翻翻开头和第一步。

第三步:

在s0和sn无法改变在序列中位置的前提下,怎样满足我们要求呢:
下面给出两种一般情况
捕获.PNG

这里假设s0 是小于sn的(如果是大于,swap一下就是一样的)那么明显第一个图的在y轴视角的叠次数更多,而第二个图的重叠次数更少,重叠次数更小说明我们所求的s[i] - s[i-1]会更大,这样才能保证是求得最大值,如果重叠次数越多那就说明压到y轴时越稠密,那么就是最大值了

分析到这里就完成了第三步,还剩下最后一步,经过我的理解,我发现了 很透彻 最后一步的讲解,为什么要跳着取而不是顺序取

完事完事,真滴难

c++代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;
const int N = 300010;
typedef long long LL;

int n;
LL a[N], s[N];
bool st[N];

int main()
{
    int t;
    scanf("%d", &t);
    while(t -- )
    {
        scanf("%d", &n);
        s[0] = 0;
        for(int i = 1; i <= n; i ++ ) 
        {
            scanf("%lld", &a[i]);
            s[i] = s[i - 1] + a[i];
        }
        
        LL s0 = s[0], sn = s[n];
        if(s0 > sn) swap(s0, sn);
        
        sort(s, s + n + 1);
        
        for(int i = 0; i <= n; i ++ )
            if(s[i] == s0) 
            {
                s0 = i;
                break;
            }
            
        for(int i = n; i >= 0; i -- )
            if(s[i] == sn)
            {
                sn = i;
                break;
            }
            
        memset(st, 0,sizeof st);
        int l = 0 ,r = n;
        
        for(int i = s0; i >= 0; i -= 2)
        {
            a[l ++ ] = s[i];
            st[i] = true;
        }
        
        for(int i = sn; i <= n; i += 2)
        {
            a[r -- ] = s[i];
            st[i] = true;
        }
        
        for(int i = 0; i <= n; i ++ )
            if(!st[i]) a[l ++ ] = s[i];
            
            
        LL res = 0;
        
        for(int i = 1; i <= n; i ++ ) res = max(res, abs(a[i] - a[i - 1]));
        
        printf("%lld\n", res);
    }
    
    return 0;
}

总结:自己太菜了

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1bu3dong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值