[TOC]
总起
2021年蓝桥杯省赛第一场4月18号举行,赛后拿到题目:
比赛前:去年题目那么简单,今年省一肯定有希望
比赛中:我参加的是蓝桥杯还是ACM?
比赛结束后:能拿省三就算成功
填空题
填空题答案一览
| 题目 |
答案 |
分值 |
| A、空间 |
6710884 |
5 |
| B、卡片 |
3181 |
5 |
| C、直线 |
40257 |
10 |
| D、货物摆放 |
2430 |
10 |
| E、路径 |
10266837 |
15 |
试题 A: 空间
题目
本题总分:5 分
【问题描述】
小蓝准备用 256MB 的内存空间开一个数组,数组的每个元素都是 32 位
二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问
256MB 的空间可以存储多少个 32 位二进制整数?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
代码
| #include <bits/stdc++.h> using namespace std;
int main(){ cout<< 1ll*256*1024*1024*8/32 << endl; return 0; }
|
试题 B: 卡片
题目
试题 B: 卡片
本题总分:5 分
【问题描述】
小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。
小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,
就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 1 拼到多少。
例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,
但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。
现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从 1
拼到多少?
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
解析
不用算所有的卡片,只需要考虑卡片1的数量,任何时候卡片1始终最先消耗完。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <bits/stdc++.h> using namespace std;
int i =1; void solve(){ int card = 2021; while(1){ int tmp = i; while(tmp){ if(tmp %10==1){ if(--card < 0){ i--;return; } } tmp /=10; } i++; } }
int main(){ solve(); cout<< i << endl; return 0; }
|
试题 C: 直线
题目
试题 C: 直线
本题总分:10 分
【问题描述】
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,
那么这些点中任意两点确定的直线是同一条。
给定平面上 2 × 3 个整点 {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},即横坐标
是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数
的点。这些点一共确定了 11 条不同的直线。
给定平面上 20 × 21 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横
坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之
间的整数的点。请问这些点一共确定了多少条不同的直线。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
解析
两两点确定的直线,用k值和b值去重
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <bits/stdc++.h> using namespace std;
vector<pair<int,int>> p; int N =20, M = 21; set<pair<double,double>>s;
void addLine(int i, int j){ int x1 = p[i].first , y1 = p[i].second; int x2 = p[j].first , y2 = p[j].second; if(x1 == x2 || y1 == y2) return; double fm = (x2 - x1) * 1.0; double k = (y2-y1)*1.0/ (x2-x1); double b = (y1*x2 - x1*y2) * 1.0 / fm; s.insert(make_pair(k,b)); } int solve(){ for(int i =0;i<N;i++) for(int j=0;j<M;j++) p.push_back(make_pair(i,j)); for(int i = 0;i<p.size();i++) for(int j = i+1;j<p.size();j++) addLine(i,j); return (s.size() + N + M); } int main(){ cout<< solve() << endl; return 0; }
|
试题 D: 货物摆放
题目
试题 D: 货物摆放
本题总分:10 分
【问题描述】
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝
规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、
宽、高。
小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上
分别堆 L、W、H 的货物,满足 n = L × W × H。
给定 n,请问有多少种堆放货物的方案满足要求。
例如,当 n = 4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、
2 × 2 × 1、4 × 1 × 1。
请问,当 n = 2021041820210418 (注意有 16 位数字)时,总共有多少种
方案?
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
方法一:枚举所有因子组合
两重循环枚举有乘积为n的三位正整数,剪枝能达到效率要求。注意去重(三个因数大小顺序)。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <bits/stdc++.h> using namespace std;
#define ll long long int main(){ ll n = 2021041820210418L; ll ans = 0; for(ll i = 1;i*i*i <=n;i++){ if(n%i != 0) continue; ll tmp = n / i; for(ll j = 1;j*j<= tmp;j++){ if(tmp % j != 0) continue; ll k = tmp / j; if(k < j || i > j) continue; if(i == j && i == k) ans +=1; else if(i == j || i==k || j==k) ans +=3; else ans += 6; } } cout<< ans <<endl; return 0; }
|
方法二:分解质因数
对n进行质因数分解,所有质因数可求出因数个数
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <bits/stdc++.h> using namespace std;
#define PB(X) push_back(X) using VI=vector<int>;
#define ll long long VI prime; bool vis[10000005]; int nums[10000005]; void shai(){ memset(vis, 0, sizeof(vis)); for(int i =2;i<10000000;i++){ if(!vis[i]){ prime.PB(i); for(int j = i+i;j<10000000;j+=i) vis[j] = 1; } } } VI v; int main(){ shai(); ll n = 2021041820210418; for(int i = 0;i<prime.size();i++){ nums[i] = 0; while(n % prime[i] == 0){ n /= prime[i]; nums[i]++; } if(nums[i] != 0) v.PB(i); } for(int i =0;i<v.size();++i){ cout<< prime[v[i]] << ' ' << nums[v[i]] <<endl; } return 0; }
|
试题 E: 路径
题目
试题 E: 路径
本题总分:15 分
【问题描述】
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图
中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点
之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条
长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无
向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案
方法一:Dijkstra
解析
最短路模板题。构建邻接矩阵,套Dijkstra模板
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <bits/stdc++.h> using namespace std;
#define ll long long #define INF 0x3f3f3f3f #define N 2025 int edges[N][N]; int d[N]; bool vis[N];
int gcd(int a, int b){return b==0? a : gcd(b, a%b);} int lcm(int a, int b){return a / gcd(a, b) * b;}
int main(){ memset(edges, INF, sizeof(edges)); for(int i = 1;i<N;i++){ edges[i][i] = 0; for(int j = i+1;j < N;j++){ int w = lcm(i, j); edges[i][j] = edges[j][i] = w; } } memset(d, INF, sizeof(d)); memset(vis, false, sizeof(vis)); d[1] = 0; for(int i = 1;i<N;i++){ int x = 0; for(int j = 1;j<N;j++) if(!vis[j] && d[j] < d[x]) x= j; vis[x] =1; for(int j = max(1, x-21); j<=min(N, x +21);j++){ d[j] = min(d[j] , d[x] + edges[x][j]); } } cout<< d[2021] <<endl; return 0; }
|
重写了一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <cstring> #include <algorithm> using namespace std;
const int N = 2022; int dist[N]; bool st[N];
int main() { memset(dist, 0x3f, sizeof dist); memset(st, false, sizeof st); 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] = true; for (int j = max(t - 21, 1); j <= min(t + 21, N - 1); j ++) dist[j] = min(dist[j], dist[t] + t / __gcd(t, j) * j); } cout << dist[2021] << endl; }
|
方法二:动态规划
解析:
由于边的特殊性(边权为两数的最小公倍数,且两数的绝对值相差不超过21才连通),那么其实从1到某点的最短路径必然是递增的,证明:略(不会)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <bits/stdc++.h> using namespace std;
#define INF 0x3f3f3f3f #define N 2025 int d[N];
int gcd(int a, int b){return b==0? a : gcd(b, a%b);} int lcm(int a, int b){return a / gcd(a, b) * b;} int main(){ memset(d, INF, sizeof(d)); d[1] =0; for(int i = 1;i<N;i++){ for(int j = i+1;j < N && j-i <= 21;j++){ d[j] = min(d[j], lcm(i, j)+d[i]); } } cout<< d[2021] <<endl; return 0; }
|
程序设计大题
试题 F: 时间显示
题目
试题 F: 时间显示
时间限制: 1.0s
内存限制: 256.0MB
本题总分:15 分
【问题描述】
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取
了当前的时间,用一个整数表示,值为从 1970 年 1 月 1 日 00:00:00 到当前时
刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要
显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
【输入格式】
输入一行包含一个整数,表示时间。
【输出格式】
输出时分秒表示的当前时间,格式形如 HH:MM:SS,其中
HH 表示时,值
为 0 到 23,MM 表示分,值为 0 到 59,SS 表示秒,值为 0 到 59。时、分、秒
不足两位时补前导 0。
【样例输入 1】
46800999
【样例输出 1】
13:00:00
【样例输入 2】
1618708103123
试题F: 时间显示
7第十二届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组
【样例输出 2】
01:08:23
【评测用例规模与约定】
对于所有评测用例,给定的时间为不超过 1018
的正整数。
解析
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <bits/stdc++.h> using namespace std;
#define ll long long
string get(int a){ return (a>9?to_string(a) : ("0" + to_string(a))); } int main(){ ll a; cin>> a; a%=(24*60*60*1000); a/=1000; int h = a/3600, m = (a%3600) / 60, s = a%60; cout<<get(h) << ":" <<get(m) <<":" << get(s) <<endl; return 0; }
|
试题 G: 砝码称重
题目
时间限制: 1.0s
内存限制: 256.0MB
本题总分:20 分
【问题描述】
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · , W**N。
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。
【输入格式】
输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1, W2, W3, · · · , W**N。
【输出格式】
输出一个整数代表答案。
【样例输入】
3
1 4 6
【样例输出】
10
【样例说明】
能称出的 10 种重量是:1、2、3、4、5、6、7、9、10、11。
1 = 1;
2 = 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。
解析
True False问题,两遍01背包
| dp[i] = dp[i] or dp[i-w] dp[i] = dp[i] or dp[i + w]
|
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include<bits/stdc++.h> #define ll long long using namespace std; int dp[100005]; int w[105]; int main(){ int n; cin>> n; for(int i = 0;i<n;i++) cin>>w[i]; memset(dp, 0, sizeof(dp)); dp[0] =1; for(int i = 0;i<n;i++){ for(int j = 100000;j>=w[i];j--){ dp[j] = max(dp[j], dp[j-w[i]]); } } for(int i = 0 ;i<n;i++){ int size = 100000 - w[i]; for(int j = 0 ;j<= size;j++){ dp[j] = max(dp[j], dp[j+w[i]]); } }
int ans = 0; for(int i = 1;i <= 100000;i++){ if(dp[i]) ans++; } cout<<ans<<endl; }
|
试题 H: 杨辉三角形(待优化
题目
时间限制: 1.0s
内存限制: 256.0MB
本题总分:20 分
【问题描述】
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下
数列:
1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, …
给定一个正整数 N,请你输出数列中第一次出现
N 是在第几个数?
【输入格式】
输入一个整数 N。
【输出格式】
输出一个整数代表答案。
【样例输入】
6
【样例输出】
13
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ N ≤ 10;
对于所有评测用例,1 ≤ N
≤ 1000000000。
解析
按题目模拟只能拿20%的分数。
代码
试题 I: 双向排序(待优化
题目
时间限制: 1.0s
内存限制: 256.0MB
本题总分:25 分
【问题描述】
给定序列 (a1, a2, · · · , a**n) = (1, 2, · · · , n),即 a**i
= i。
小蓝将对这个序列进行 m 次操作,每次可能是将 a1, a2, · · · , aqi 降序排列,
或者将 aqi , aqi+1, · · · , a**n 升序排列。
请求出操作完成后的序列。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示序列的长度和操作次数。
接下来 m 行描述对序列的操作,其中第 i 行包含两个整数 p**i, q**i 表示操作
类型和参数。当 p**i = 0 时,表示将 a1, a2, · · · , aqi 降序排列;当 p**i = 1 时,表示
将 aqi , aqi+1, · · · , a**n 升序排列。
【输出格式】
输出一行,包含 n
个整数,相邻的整数之间使用一个空格分隔,表示操作
完成后的序列。
【样例输入】
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 ≤ a**i
≤ 1,1 ≤ b**i ≤ n。
解析
sort模拟题目,只能过60%的数据
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <bits/stdc++.h> using namespace std;
#define maxn 100005 int a[maxn]; int n , m; int main(){ cin>> n >> m; for(int i =0;i<n;i++) a[i] = i+1; int p , q; while(m--){ cin>>p>>q; if(p){ sort(a+q-1,a+n); }else{ sort(a, a+q, [](int &a,int &b){ return a>b; }); } } for(int i =0;i<n-1;i++){ cout<<a[i]<<" "; } cout<<a[n-1]<<endl; return 0; }
|
试题 J: 括号序列(待更
题目
解析
代码