第十三届蓝桥杯C++ B组 赛后总结&不完全题解

Posted mp-ui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十三届蓝桥杯C++ B组 赛后总结&不完全题解相关的知识,希望对你有一定的参考价值。

总体上看,感觉今年的省赛题比去年的难,下面是我的个人题解,大佬勿喷
真题链接:https://www.dotcpp.com/oj/train/1026/

A. 九进制转十进制


这道是送分题了,2*(9**3) + 2*9 + 2 = 1478

B. 顺子日期


这道题有歧义,鬼知道012 210 321算不算,我填了4种

C. 刷题统计


先算出需要多少个星期,再算一下还要多多少天

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() 
	ll a,b,n;
	cin >> a >> b >> n;
	ll week = a * 5 + b * 2;
	ll m = n / week * 7;
	ll mm = n / week * week;
	int i = 0;
	while(mm < n) 
		if(i <= 4) 
			mm += a;	
		 else 
			mm += b;
		
		++i;
		++m;
	
	cout << m << endl;
	return 0;
 

运行结果:

D. 修剪灌木


这题换句话说就是从这个位置出发,向左走再走回来和向右走再走回来,哪个距离大

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() 
	int n;
	cin >> n;
	for(int i = 0; i < n; ++i) 
		cout << max(i * 2, (n - i - 1) * 2) << endl;
	
	return 0;
 

运行结果:

E. X进制减法




这道题的难点就是X进制怎么转换成10进制
比如题目的321,从左到右分别是8/10/2进制,那么转成十进制应该是1 + 2 * (2) + 3 * (10 * 2),每一个数位都是乘上右边所有数的进制数相乘。
我的做法是贪心,不知道对不对,题目说了A>B,那肯定是进制越小最后的差越小
所以我的每一位的进制数 都是A和B这个位的数值 大的那个 加一

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

int main() 
	int n;
	cin >> n;
	int ma, mb;
	cin >> ma;
	vector<int> aa(ma, 0);
	for(int i = 0; i < ma; ++i) 
		cin >> aa[i];
	
	cin >> mb;
	vector<int> bb(mb, 0);
	for(int i = 0; i < mb; ++i) 
		cin >> bb[i];
	
	// ======================================
	int mab = max(ma, mb);
	vector<int> a(mab, 0);
	vector<int> b(mab, 0);
	for(int i = 0; i < ma; ++i) 
		a[ma - i - 1] = aa[i];
	
	for(int i = 0; i < mb; ++i) 
		b[mb - i - 1] = bb[i];
	
	ll res = 0;
	ll p = 1;
	for(int i = 0; i < mab; ++i) 
		res += (a[i] - b[i]) * p;
		res %= mod;
		p *= max(max(a[i], b[i]) + 1, 2);
		p %= mod;
	
	cout << res << endl;
	return 0;
 

运行结果:

F. 统计子矩阵




我是直接前缀和暴力了,不出意外肯定超时,太菜了想来想去就是想不出该怎么优化。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() 
	ll m,n,k;
	cin >> m >> n >> k;
	vector<vector<int> > v(m, vector<int>(n));
	for(int i = 0; i < m; ++i) 
		for(int j = 0; j < n; ++j) 
			cin >> v[i][j];
		
	
	vector<vector<int> > p(m + 1, vector<int>(n + 1));
	for(int i = 1; i <= m; ++i) 
		for(int j = 1; j <= n; ++j) 
			p[i][j] = p[i - 1][j] + p[i][j-1] - p[i-1][j-1] + v[i-1][j-1];
		
	
	ll res = 0;
	for(int i = 1; i <= m; ++i) 
		for(int j = 1; j <= n; ++j) 
			for(int a = 0; a < i; ++a) 
				for(int b = 0; b < j; ++b) 
					if(p[i][j] - p[i][b] - p[a][j] + p[a][b] <= k) ++res;
				
			
		
	
	cout << res << endl;
	return 0;
 

运行结果:果然是超时了

G. 积木画




这个是经典DP了,有手就行,力扣有原题:https://leetcode-cn.com/problems/domino-and-tromino-tiling/

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

int main() 
	ll n;
	cin >> n;
	vector<ll> dp(n+1);
	dp[0] = 1;
	ll p = 0;
	for(int i = 1; i <= n; ++i) 
		// I 竖着摆 
		dp[i] += dp[i-1];
		dp[i] %= mod;
		if(i >= 2) 
			// 两个I横着摆 
			dp[i] += dp[i-2];
			dp[i] %= mod;
		
		// 两个L型或者两个L型夹着若干个横着的I型 
		dp[i] += p * 2;
		dp[i] %= mod;
		if(i >= 2) 
			p += dp[i-2];
			p %= mod;
		
	
	cout << dp[n] << endl;
	return 0;
 

运行结果:

H. 扫雷




这题我的做法就是先建图,然后DFS,有一点注意的是他的数据量很大,建图的时候如果两层循环遍历,不出意外的话肯定会超时,但是可以发现r是很小的,所以我的做法是用两层的map,记录这个位置有哪些炸弹,然后枚举他附近的位置。

这题让我有点疑惑的是,如果有第二个排雷火箭,那第一次被炸掉了第二次还算不算,应该不算了吧。。

力扣上面有道很像的:https://leetcode-cn.com/problems/detonate-the-maximum-bombs/

这道好像没做出来,代码就不放了

I. 李白打酒




这题应该就是个DP,我做的时候没那么多时间了,直接丢了一个暴力就提交上去了,后面回去我补了一个DP的版本

暴力版本:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
map<ll, int> dp[110][110];
const ll mod = 1000000007;

int f(int i, int j, ll k) 
	if(i == 0) 
		return j == k;
	
	if(dp[i][j].find(k) != dp[i][j].end()) 
		return dp[i][j][k];
	
	if(i > 0)
		dp[i][j][k] += f(i-1 ,j ,k * 2);
		dp[i][j][k] %= mod;
	
	if(j > 1) 
		dp[i][j][k] += f(i, j-1, k - 1);
		dp[i][j][k] %= mod;
	
	return dp[i][j][k];


int main() 
	cin >> n >> m;
	cout << f(n,m,2) << endl;
	return 0;
 

运行结果:超时了

DP版本:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;

int main() 
	int n,m;
	cin >> n >> m;
	vector<vector<vector<int > > > dp(n + 1, vector<vector<int> >(m + 1,vector<int>(m + 1)));
	dp[0][1][1] = 1;
	for(int i = 0; i <= n; ++i) 
		for(int j = 0; j <= m; ++j) 
			for(int k = 0; k <= m; ++k) 
				if(k > 0 && j >= 1) 
					dp[i][j][k] += dp[i][j-1][k-1];
				
				if(k * 2 <= m && i >= 1) 
					dp[i][j][k] += dp[i-1][j][k*2];
				
				dp[i][j][k] %= mod;
			
		
	
	cout << dp[n][m][2] << endl;
	return 0;
 

运行结果:已通过

J. 砍竹子



贪心,用一个优先队列,每次都是挑最大的下手,处理这个数的时候再向左右两边扩散,高度一样的一起干了。

发现一个规律,那些数只有最开始的时候是随机的,只要经过一次sqrt(x / 2 + 1)的处理,这个数以后都是1/6/16/30/…,一个数和他旁边的数一旦相等,以后永远都相等,所以,我对一个数进行sqrt(x / 2 + 1)处理之后,会将它重新放回优先队列,如果他隔壁的数和他原来相等,我会顺便对他隔壁的数进行sqrt(x / 2 + 1)处理,但是不会再放回优先队列。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

struct P
	ll x;
	int idx;
	
	P(ll a, int b) : x(a), idx(b)
	
	bool operator<(const P& p) const 
		return this->x < p.x;
	
;

int main() 
	int n;
	cin >> n;
	vector<ll> v(n);
	for(int i = 0; i < n; ++i) cin >> v[i];
	priority_queue<P, vector<P> > pq;
	for(int i = 0; i < n; ++i) 
		if(v[i] > 1) 
			pq.push(P(v[i], i));
		
	
	ll res = 0;
	while(!pq.empty()) 
		P p = pq.top();
		pq.pop();
		if(v[p.idx] != p.x) 
			continue;
		
		++res;
		ll x = p.x;
		int idx = p.idx;
		v[idx] = sqrt(x / 2 + 1);
		if(v[idx] > 1) 
			pq.push(P(v[idx], idx));
		
		int i = idx - 1;
		while(i >= 0 && v[i] == x) 
			v[i--] = sqrt(x / 2 + 1);
		
		i = idx + 1;
		while(i < n && v[i] == x) 
			v[i++] = sqrt(x / 2 + 1);
		
	
	cout << res << endl;
	return 0;
 

运行结果:

总结:希望混个省一,赚回报名费

以上是关于第十三届蓝桥杯C++ B组 赛后总结&不完全题解的主要内容,如果未能解决你的问题,请参考以下文章

第十三届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组思考+总结

第十三届蓝桥杯C++ B组 不完全题解

第十三届蓝桥杯国赛 C++ C组 F 题Python B组 E 题——近似GCD(AC)

第十三届蓝桥杯省赛 C++ C 组 I 题Python B 组 H题——技能升级(AC)

第十三届蓝桥杯 C/C++ 大学B组 题解

2022 第十三届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解