第十三届蓝桥杯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;
} 

运行结果:
在这里插入图片描述

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值