第十二届蓝桥杯第一场C++B组

[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 位二进制整数?

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一

个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案

1
67108864

代码

1
2
3
4
5
6
7
#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
3181

解析

不用算所有的卡片,只需要考虑卡片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) 之

间的整数的点。请问这些点一共确定了多少条不同的直线。

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一

个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案

1
40257

解析

两两点确定的直线,用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; //set的作用是去重

//将两点确定的直线的斜率和截距放进set里
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 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝

规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、

宽、高。

小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上

分别堆 LWH 的货物,满足 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 位数字)时,总共有多少种

方案?

提示:建议使用计算机编程解决问题。

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一

个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案

1
2430

方法一:枚举所有因子组合

两重循环枚举有乘积为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,如果 ab 的差的绝对值大于 21,则两个结点

之间没有边相连;如果 ab 的差的绝对值小于等于 21,则两个点之间有一条

长度为 ab 的最小公倍数的无向边相连。

例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无

向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。

请计算,结点 1 和结点 2021 之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题。

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一

个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

答案

1
10266837

方法一: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));//edges数组所有元素初始化为INF
//邻接矩阵
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)); //d数组所有元素初始化为INF
memset(vis, false, sizeof(vis)); //vis数组所有元素初始化为false
d[1] = 0;
//Dijkstra
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背包

1
2
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%的分数。

代码

1

试题 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**in

解析

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){
//p=1 q~n 升序
sort(a+q-1,a+n);
}else{
//p=0 1~q 降序
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: 括号序列(待更

题目

解析

代码

1


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!