第十三届蓝桥杯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++ C组 F 题Python B组 E 题——近似GCD(AC)