前几天(2021.4.18)刚刚比完了2021年第十二届蓝桥杯省赛,本人参加的是软件组C++B组的比赛,本文包括了这一届C++B组的题目以及部分题解、感悟和总结。
试题A.空间
考查计算机基础知识,一字节等于8位,1MB=220B
答案:67108864
256*2^20/4
试题B.卡片
一道模拟题,注意题目要求求出能够拼到多少,而不是求不够拼出多少,最后结果要减1
答案:3181
#include <iostream>
using namespace std;
int cnt[15];
int main()
{
for(int i=0;i<=9;i++) cnt[i]=2021;
int i;
for(i=1;;i++){
int t=i;
while(t){
if(cnt[t%10]==0){
cout<<i-1;
return 0;
}
cnt[t%10]--;
t/=10;
}
}
return 0;
}
试题C.直线
根据直线两点式推导转换成直线一般方程ax+by+c=0(见下图)这样就不用考虑斜率是否存在、避免除法的困扰了,通过除以公约数使a,b,c互质,放入set去重就行了,但是要重载操作符。
答案:40257
#include<iostream>
#include<cmath>
#include<set>
using namespace std;
struct node{//点
int x,y;
}p[1000];
struct line{//直线
int a,b,c;//直线一般方程的系数
bool operator<(const line &p) const {
if (a == p.a) return b == p.b ? c < p.c : b < p.b;
return a < p.a;
}
bool operator==(const line &p) const {
return a == p.a && b == p.b && c == p.c;
}
};
int cnt;
set<line> se;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
int gcdd(int a,int b,int c){
return gcd(gcd(a,b),gcd(b,c));
}
int main()
{
int n=20,m=21;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
p[++cnt]={i,j};
for(int i=1;i<=cnt;i++){
for(int j=i+1;j<=cnt;j++){
int a=p[i].y-p[j].y;//系数
int b=p[j].x-p[i].x;
int c=p[i].y * (p[i].x-p[j].x)- p[i].x *(p[i].y-p[j].y);
int t=gcdd(fabs(a),fabs(b),fabs(c));
se.insert({a/t,b/t,c/t});
}
}
cout<<se.size();
return 0;
}
试题D.货物摆放
题目给的数很大,如果直接暴力两重循环会超时。转换思路,把n所有的约数求出来,发现 2021041820210418只有128个约数,然后对这128个约数暴力枚举两重循环,计算出结果。可惜这题比赛的时候思路歪了想错了。
答案:2430
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL yue[101000],cnt;
int main()
{
LL n=2021041820210418;
for(LL i=1;i<=n/i;i++){
if(n%i==0){
yue[++cnt]=i;
if(i*i!=n)
yue[++cnt]=n/i;
}
}
//sort(yue+1,yue+cnt+1);
//for(int i=1;i<=cnt;i++)cout<<yue[i]<<" ";
//cout<<cnt;
int ans=0;
for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++){
if(n%(yue[i]*yue[j])==0)
ans++;
}
}
cout<<ans;
return 0;
}
试题E.路径
最短路径模版题,dijkstra跑一遍就行了。比赛的时候太赶时间了做完就直接把答案交上去了最后还忘记检查,太懊悔了,比赛结束发现我交的答案竟然是0x3f3f3f3f,我TM直接心态崩了。大家做完一定要好好检查!!!
答案:10266837
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int N=2510;
int g[N][N],dist[N],st[N];
int n=2021;
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
int lcm(int a,int b){
return a*b/gcd(a,b);
}
int dijkstra(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=1;i<=n;i++){
int t=-1;
for(int j=1;j<=n;j++){
if(!st[j] && (t==-1 || dist[j]<dist[t]))
t=j;
}
st[t]=1;
for(int j=1;j<=n;j++){
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
}
return dist[n];
}
int main(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i!=j){
if(fabs(i-j)<=21){
g[i][j]=lcm(i,j);
g[j][i]=lcm(i,j);
}
else{
g[i][j]=0x3f3f3f3f;
g[j][i]=0x3f3f3f3f;
}
}
}
cout<<dijkstra();
//cout<<0x3f3f3f3f;
return 0;
}
试题F.时间显示
【样例输入 1】
46800999
【样例输出 1】
13:00:00
【样例输入 2】
1618708103123
【样例输出 2】
01:08:23
【评测用例规模与约定】
对于所有评测用例,给定的时间为不超过 1018 的正整数。
这算是一道相对比较简单的题了,也是唯一完整做出来的了,除法取模搞定,注意要用longlong。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
int main()
{
LL n;
cin>>n;
n/=1000;
int h=n/3600%24;
n=n%3600;
int m=n/60%60;
n=n%60;
int s=n%60;
printf("%02d:%02d:%02d",h,m,s);
return 0;
}
试题G.砝码称重
【样例输入】
3
1 4 6
【样例输出】
10
【样例说明】
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 6 6 4 (天平一边放 6,另一边放 4);
3 = 4 1;
4 = 4;
5 = 6 1;
6 = 6;
7 = 1 + 6;
9 = 4 + 6 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 15。
对于所有评测用例,1 ≤ N ≤ 100,N 个砝码总重不超过 100000。
嗯?不对劲哦,第二题就不会了,01背包?太菜了想不明白。
后补:闫氏dp分析法
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110, M = 2e5 + 10;
int n,m;//m记录最大重量
int a[N];
bool dp[N][M];//dp[i][j]表示前i个砝码,称出j的集合,值为bool值,能称出j就true
//砝码称重
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],m+=a[i];
dp[0][0]=true;
for (int i = 1; i <= n;i++)//前i个
for (int j = 0; j <=m;j++)//称出j
dp[i][j]=dp[i-1][j]||dp[i-1][j+a[i]]||dp[i-1][abs(j-a[i])];
//只要有一种情况为真,那么dp[i][j]就真
int ans=0;
for(int i=1;i<=m;i++)
if(dp[n][i])
ans++;
cout<<ans;
return 0;
}
试题H.杨辉三角形
【样例输入】
6
【样例输出】
13
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ N ≤ 10;
对于所有评测用例,1 ≤ N ≤ 1000000000。
这数据开的也太大了吧,109次方,没办法了暴力前十个骗分。
试题I.双向排序
【样例输入】
3 3
0 3
1 2
0 2
【样例输出】
3 1 2
【样例说明】
原数列为 (1, 2, 3)。 第 1 步后为 (3, 2, 1)。 第 2 步后为 (3, 1, 2)。 第 3 步后为 (3, 1, 2)。与第 2 步操作后相同,因为前两个数已经是降序了。
【评测用例规模与约定】
对于 30% 的评测用例,n, m ≤ 1000;
对于 60% 的评测用例,n, m ≤ 5000;
对于所有评测用例,1 ≤ n, m ≤ 100000,0 ≤ ai ≤ 1,1 ≤ bi ≤ n。
上来直接用sort,时间复杂度O(mnlogn),一半分应该能拿到
#include<iostream>
#include<algorithm>
using namespace std;
int a[101000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)a[i]=i;
while(m--){
int p,q;
cin>>p>>q;
if(p==0){
sort(a+1,a+q+1,greater<int>());
}else{
sort(a+q,a+n+1);
}
}
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
return 0;
}
试题J.括号序列
【样例输入】
((()
【样例输出】
5
【评测用例规模与约定】
对于 40% 的评测用例,|s| ≤ 200。
对于所有评测用例,1 ≤ |s| ≤ 5000。
好像也是用dp,不会做
yxc讲解:括号序列
总结
这次比赛感觉比想象的难一点,编程第二题就涉及了DP,做过以往的题目不会像今年直接编程第二题就无从下手,DP也只会几个经典的模版题,属实想不到,括号那题好像也是DP,真没法做,而且这一块练的也少新的背景根本想不到,更别说列出转移方程。最可气的还是路径那题,标准最短路径模版题,竟然拿不到分!!还是怪自己太粗心、不检查。总得来说虽然这次蓝桥杯拿不到好成绩,有点小遗憾,但是总归在准备过程中还是学到了不少东西。