2024年第十五届蓝桥杯C++B组
博客地址:https://blog.csdn.net/m0_46326495/article/details/137728637
A: 握手问题(5分)
问题描述
小蓝组织了一场算法交流会议,总共有
50
50
50 人参加了本次会议。在会议上,大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手(且仅有一次)。但有
7
7
7 个人,这7 人彼此之间没有进行握手(但这
7
7
7 人与除这
7
7
7 人以外的所有人进行了握手)。请问这些人之间一共进行了多少次握手?
注意
A
A
A 和
B
B
B 握手的同时也意味着
B
B
B 和
A
A
A 握手了,所以算作是一次握手。
思路
组合数学口算题, C 50 2 − C 7 2 = 1204 C_{50}^2-C_{7}^2=1204 C502−C72=1204
B: 小球反弹(5分)
问题描述
有一长方形,长为 343720 343720 343720 单位长度,宽为 233333 233333 233333 单位长度。在其内部左上角顶点有一小球(无视其体积),其初速度如图所示且保持运动速率不变,分解到长宽两个方向上的速率之比为 d x : d y = 15 : 17 dx : dy = 15 : 17 dx:dy=15:17。小球碰到长方形的边框时会发生反弹,每次反弹的入射角与反射角相等,因此小球会改变方向且保持速率不变(如果小球刚好射向角落,则按入射方向原路返回)。从小球出发到其第一次回到左上角顶点这段时间里,小球运动的路程为多少单位长度?答案四舍五入保留两位小数。
思路
也是数学题,最终返回左上角时,走过的水平路程和垂直路程一定是 343720 343720 343720和 233333 233333 233333的偶数倍,并且水平路程与垂直路程之比一定为 15 : 17 15:17 15:17。写暴力去找结果即可,答案是 1100325199.77 1100325199.77 1100325199.77
代码:
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const ll W = 233333;
const ll L = 343720;
int main() {
for (ll x = 2; x <= 10000; x += 2) {
for (ll y = 2; y <= 10000; y += 2) {
if (15 * W * y == 17 * L * x) {
printf("%llf", sqrt((L * x) * (L * x) + (W * y) * (W * y)));
return 0;
}
}
}
return 0;
}
C: 好数(10分)
问题描述
一个整数如果按从低位到高位的顺序,奇数位(个位、百位、万位· · · )上的数字是奇数,偶数位(十位、千位、十万位· · · )上的数字是偶数,我们就称之为“好数”。给定一个正整数 N N N,请计算从 1 1 1 到 N N N 一共有多少个好数。
输入格式
一个整数 N N N
输出格式
一个整数代表答案
样例输入1
24
样例输出1
7
样例输入2
2024
样例输出2
150
样例说明
对于第一个样例, 24 24 24 以内的好数有 1 1 1、 3 3 3、 5 5 5、 7 7 7、 9 9 9、 21 21 21、 23 23 23,一共 7 7 7 个。
评测用例规模与约定
对于
10
%
10\%
10% 的评测用例,
1
≤
N
≤
1
0
2
1 ≤ N ≤ 10^2
1≤N≤102
对于
100
%
100\%
100% 的评测用例,
1
≤
N
≤
1
0
7
1 ≤ N ≤ 10^7
1≤N≤107
思路
正解应该是数位dp,但是感觉数据不大,暴力枚举也能过
#include <iostream>
#include <algorithm>
using namespace std;
int n;
bool inline judge(int x) {
int k = 1;
while (x) {
if ((x & 1) != (k & 1)) {
return false;
}
x /= 10;
k++;
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
cin >> n;
int res = 0;
for (int i = 1; i <= n; i += 2) if (judge(i)) res++;
cout << res << endl;
return 0;
}
D: R 格式(10分)
问题描述
小蓝最近在研究一种浮点数的表示方法: R R R 格式。对于一个大于 0 0 0 的浮点数 d d d,可以用 R R R 格式的整数来表示。给定一个转换参数 n n n,将浮点数转换为 R R R格式整数的做法是:
- 将浮点数乘以 2 n 2^n 2n;
- 四舍五入到最接近的整数。
输入格式
一行输入一个整数 n n n 和一个浮点数 d d d,分别表示转换参数,和待转换的浮点数
输出格式
输出一行表示答案: d d d 用 R R R 格式表示出来的值
样例输入
2 3.14
样例输出
13
样例说明
3.14 × 2 2 = 12.56 3.14 × 2^2 = 12.56 3.14×22=12.56,四舍五入后为 13 13 13
评测用例规模与约定
对于
50
%
50\%
50% 的评测用例,
1
≤
n
≤
10
1 ≤ n ≤ 10
1≤n≤10,
1
≤
1 ≤
1≤将
d
d
d 视为字符串时的长度
≤
15
≤ 15
≤15。
对于
100
%
100\%
100% 的评测用例,
1
≤
n
≤
1000
1 ≤ n ≤ 1000
1≤n≤1000,
1
≤
1 ≤
1≤将
d
d
d 视为字符串时的长度
≤
1024
≤ 1024
≤1024;保证
d
d
d 是小数,即包含小数点。
思路
高精度
#include <iostream>
#include <algorithm>
using namespace std;
string add(string a, string b) {
string res;
int carry = 0;
int i = a.size() - 1;
int j = b.size() - 1;
while (i >= 0 || j >= 0) {
int sum = carry;
if (i >= 0) sum += a[i--] - '0';
if (j >= 0) sum += b[j--] - '0';
carry = sum / 10;
res += to_string(sum % 10);
}
if (carry) res += to_string(carry);
reverse(res.begin(), res.end());
return res;
}
string mul(string a, string b) {
string res = "0";
int n = a.size();
int m = b.size();
for (int i = n - 1; i >= 0; i--) {
int carry = 0;
string temp;
for (int j = m - 1; j >= 0; j--) {
int sum = (a[i] - '0') * (b[j] - '0') + carry;
carry = sum / 10;
temp += to_string(sum % 10);
}
if (carry) temp += to_string(carry);
reverse(temp.begin(), temp.end());
for (int k = 0; k < n - 1 - i; k++) temp += "0";
res = add(res, temp);
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
int n;
string d;
cin >> n >> d;
string res = "1";
for (int i = 0; i < n; i++) res = mul(res, "2");
int pos = 0;
while (d[d.size() - 1 - pos] != '.') pos++;
res = mul(res, d.substr(0, d.size() - pos - 1) + d.substr(d.size() - pos));
string remain = "5";
for (int i = 0; i < pos - 1; i++) remain += "0";
res = add(res, remain);
cout << res.substr(0, res.size() - pos) << endl;
return 0;
}
E: 宝石组合(15分)
问题描述
在一个神秘的森林里,住着一个小精灵名叫小蓝。有一天,他偶然发现了一个隐藏在树洞里的宝藏,里面装满了闪烁着美丽光芒的宝石。这些宝石都有着不同的颜色和形状,但最引人注目的是它们各自独特的“闪亮度” 属性。每颗宝石都有一个与生俱来的特殊能力,可以发出不同强度的闪光。小蓝共找到了
N
N
N 枚宝石,第
i
i
i 枚宝石的“闪亮度” 属性值为
H
i
H_i
Hi,小蓝将会从这
N
N
N 枚宝石中选出三枚进行组合,组合之后的精美程度
S
S
S 可以用以下公式来衡量:
S
=
H
a
H
b
H
c
×
L
C
M
(
H
a
,
H
b
,
H
c
)
L
C
M
(
H
a
,
H
b
)
×
L
C
M
(
H
a
,
H
c
)
×
L
C
M
(
H
b
,
H
c
)
S=H_aH_bH_c\times \frac{LCM(H_a,H_b,H_c)}{LCM(H_a,H_b)\times LCM(H_a,H_c)\times LCM(H_b,H_c)}
S=HaHbHc×LCM(Ha,Hb)×LCM(Ha,Hc)×LCM(Hb,Hc)LCM(Ha,Hb,Hc)
其中
L
C
M
LCM
LCM 表示的是最小公倍数函数。
小蓝想要使得三枚宝石组合后的精美程度
S
S
S 尽可能的高,请你帮他找出精美程度最高的方案。如果存在多个方案
S
S
S 值相同,优先选择按照
H
H
H 值升序排列后字典序最小的方案。
输入格式
第一行包含一个整数
N
N
N 表示宝石个数。
第二行包含
N
N
N 个整数表示
N
N
N 个宝石的“闪亮度”。
输出格式
输出一行包含三个整数表示满足条件的三枚宝石的“闪亮度”。
样例输入
5
1 2 3 4 9
样例输出
1 2 3
评测用例规模与约定
对于
30
%
30\%
30% 的评测用例,
3
≤
N
≤
100
,
1
≤
H
i
≤
1000
3 ≤ N ≤ 100, 1 ≤H_i ≤1000
3≤N≤100,1≤Hi≤1000
对于
60
%
60\%
60% 的评测用例,
3
≤
N
≤
2000
3 ≤ N ≤ 2000
3≤N≤2000
对于
100
%
100\%
100% 的评测用例,
3
≤
N
≤
1
0
5
,
1
≤
H
i
≤
1
0
5
3 ≤ N ≤ 10^5, 1 ≤H_i ≤ 10^5
3≤N≤105,1≤Hi≤105
思路
找最大gcd(a[i],a[j],a[k]),里面的最小的三个数
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int a[100100], b[100100];
vector<int> vec[100100];
int n;
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
cin >> n;
int max_a = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[a[i]]++;
max_a = max(max_a, a[i]);
}
sort(a + 1, a + 1 + n);
for (int i = 2; i <= max_a; i++) {
for (int j = i; j <= max_a; j += i) {
for (int k = 1; k <= b[j]; k++) {
if (vec[i].size() >= 3) break;
vec[i].push_back(j);
}
}
}
for (int i = max_a; i >= 2; i--) {
if (vec[i].size() >= 3) {
cout << vec[i][0] << " " << vec[i][1] << " " << vec[i][2] << endl;
return 0;
}
}
cout << a[1] << " " << a[2] << " " << a[3] << endl;
return 0;
}
F: 数字接龙(15分)
问题描述
小蓝最近迷上了一款名为《数字接龙》的迷宫游戏,游戏在一个大小为 N × N N × N N×N 的格子棋盘上展开,其中每一个格子处都有着一个 0... K − 1 0 ... K − 1 0...K−1之间的整数。游戏规则如下:
- 从左上角 ( 0 , 0 ) (0, 0) (0,0) 处出发,目标是到达右下角 ( N − 1 , N − 1 ) (N − 1,N − 1) (N−1,N−1) 处的格子,每一步可以选择沿着水平/垂直/对角线方向移动到下一个格子。
- 对于路径经过的棋盘格子,按照经过的格子顺序,上面的数字组成的序列要满足: 0 , 1 , 2 , . . . , K − 1 , 0 , 1 , 2 , . . . , K − 1 , 0 , 1 , 2 , . . . 0,1,2, ... , K − 1, 0, 1,2, ... ,K − 1, 0, 1, 2, ... 0,1,2,...,K−1,0,1,2,...,K−1,0,1,2,...。
- 途中需要对棋盘上的每个格子恰好都经过一次(仅一次)。
- 路径中不可以出现交叉的线路。例如之前有从 ( 0 , 0 ) (0, 0) (0,0) 移动到 ( 1 , 1 ) (1, 1) (1,1),那么再从 ( 1 , 0 ) (1, 0) (1,0) 移动到 ( 0 , 1 ) (0, 1) (0,1) 线路就会交叉。
为了方便表示,我们对可以行进的所有八个方向进行了数字编号,如下图 2 2 2 所示;因此行进路径可以用一个包含 0...7 0... 7 0...7 之间的数字字符串表示,如下图 1 1 1是一个迷宫示例,它所对应的答案就是: 41255214 41255214 41255214。
现在请你帮小蓝规划出一条行进路径并将其输出。如果有多条路径,输出字典序最小的那一个;如果不存在任何一条路径,则输出
−
1
−1
−1。
输入格式
第一行包含两个整数
N
N
N、
K
K
K。
接下来输入
N
N
N 行,每行
N
N
N 个整数表示棋盘格子上的数字。
输出格式
输出一行表示答案。如果存在答案输出路径,否则输出 − 1 −1 −1。
样例输入
3 3
0 2 0
1 1 1
2 0 2
样例输出
41255214
样例说明
行进路径如图 1 1 1 所示。
评测用例规模与约定
对于
80
%
80\%
80% 的评测用例,
1
≤
N
≤
5
1 ≤ N ≤ 5
1≤N≤5
对于
100
%
100\%
100% 的评测用例,
1
≤
N
≤
10
,
1
≤
K
≤
10
1 ≤ N ≤ 10, 1 ≤ K ≤ 10
1≤N≤10,1≤K≤10
思路
dfs
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int dx[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
const int dy[8] = {0, 1, 1, 1, 0, -1, -1, -1};
int n, k;
bool st[101][101], ss[11][11][11][11];
int a[101][101];
vector<string> s;
void dfs(int x, int y, string q) {
if (x == n - 1 && y == n - 1) {
if (q.size() == n * n - 1) {
s.push_back(q);
}
return;
}
for (int i = 0; i < 8; i++) {
int ax = x + dx[i], ay = y + dy[i];
if (ax < 0 || ay < 0 || ax >= n || ay >= n) continue;
if ((a[ax][ay]) != (a[x][y] + 1) % k) continue;
if (!st[ax][ay] && i % 2 == 0) {
st[ax][ay] = true;
char qq = i + '0';
dfs(ax, ay, q + qq);
st[ax][ay] = false;
} else if (!st[ax][ay] && i % 2 == 1 && !ss[x][y][ax][ay]) {
st[ax][ay] = true;
char qq = i + '0';
if (i == 1) {
ss[x - 1][y][ax + 1][ay] = true;
ss[ax + 1][ay][x - 1][y] = true;
}
if (i == 3 || i == 5) {
ss[x + 1][y][ax - 1][ay] = true;
ss[ax - 1][ay][x + 1][y] = true;
}
dfs(ax, ay, q + qq);
st[ax][ay] = false;
if (i == 1) {
ss[x - 1][y][ax + 1][ay] = false;
ss[ax + 1][ay][x - 1][y] = false;
}
if (i == 3 || i == 5) {
ss[x + 1][y][ax - 1][ay] = false;
ss[ax - 1][ay][x + 1][y] = false;
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
cin >> n >> k;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
}
}
string q;
st[0][0] = true;
dfs(0, 0, q);
if (s.empty()) {
cout << "-1";
} else {
sort(s.begin(), s.end());
cout << s[0];
}
return 0;
}
G: 爬山(20分)
问题描述
小明这天在参加公司团建,团建项目是爬山。在
x
x
x 轴上从左到右一共有
n
n
n座山,第
i
i
i 座山的高度为
h
i
h_i
hi。他们需要从左到右依次爬过所有的山,需要花费的体力值为
S
=
∑
i
=
1
n
h
i
S=\sum^n_{i=1}h_i
S=∑i=1nhi。
然而小明偷偷学了魔法,可以降低一些山的高度。他掌握两种魔法,第一种魔法可以将高度为H 的山的高度变为
⌊
h
⌋
⌊\sqrt {h}⌋
⌊h⌋,可以使用
P
P
P 次;第二种魔法可以将高度为
H
H
H 的山的高度变为
⌊
H
2
⌋
⌊\frac{H}{2}⌋
⌊2H⌋,可以使用
Q
Q
Q 次。并且对于每座山可以按任意顺序多次释放这两种魔法。
小明想合理规划在哪些山使用魔法,使得爬山花费的体力值最少。请问最优情况下需要花费的体力值是多少?
输入格式
输入共两行。
第一行为三个整数
n
n
n,
P
P
P,
Q
Q
Q。
第二行为
n
n
n 个整数
h
1
,
h
2
,
.
.
.
,
h
n
h_1,h_2,... ,h_n
h1,h2,...,hn。
输出格式
输出共一行,一个整数代表答案。
样例输入
4 1 1
4 5 6 49
样例输出
18
样例说明
将第四座山变为
⌊
49
⌋
=
7
⌊\sqrt{49}⌋ = 7
⌊49⌋=7,然后再将第四座山变为
⌊
7
2
⌋
=
3
⌊ \frac{7}{2}⌋ = 3
⌊27⌋=3。
体力值为
4
+
5
+
6
+
3
=
18
4 + 5 + 6 + 3 = 18
4+5+6+3=18。
评测用例规模与约定
对于
20
%
20\%
20%的评测用例,保证
n
≤
8
,
P
=
0
n ≤ 8,P = 0
n≤8,P=0。
对于
100
100%
100 的评测用例,保证
n
≤
100000
,
0
≤
P
≤
n
,
0
≤
Q
≤
n
,
0
≤
h
i
≤
100000
n ≤ 100000,0 ≤ P ≤ n,0 ≤ Q ≤ n,0 ≤ h_i ≤ 100000
n≤100000,0≤P≤n,0≤Q≤n,0≤hi≤100000。
思路
贪心,维护一个堆,哪个魔法减少的更多就取哪个。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
typedef long long ll;
priority_queue<int, vector<int>, less<int>> pq;
int p, q;
int n;
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
cin >> n >> p >> q;
for (int i = 0; i < n; i++) {
int temp;
cin >> temp;
pq.push(temp);
}
while (p != 0 && q != 0) {
int cur = pq.top();
pq.pop();
if ((int) sqrt(cur) < (int) (cur / 2)) {
p--;
cur = (int) sqrt(cur);
} else {
q--;
cur = (int) (cur / 2);
}
pq.push(cur);
}
while (p != 0) {
int cur = pq.top();
pq.pop();
p--;
cur = (int) sqrt(cur);
pq.push(cur);
}
while (q != 0) {
int cur = pq.top();
pq.pop();
q--;
cur = (int) (cur / 2);
pq.push(cur);
}
ll ans = 0;
while (!pq.empty()) {
ans += pq.top();
pq.pop();
}
cout << ans << endl;
return 0;
}
H: 拔河(20分)
问题描述
小明是学校里的一名老师,他带的班级共有
n
n
n 名同学,第
i
i
i 名同学力量值为
a
i
a_i
ai。在闲暇之余,小明决定在班级里组织一场拔河比赛。
为了保证比赛的双方实力尽可能相近,需要在这
n
n
n 名同学中挑选出两个队伍,队伍内的同学编号连续:
{
a
l
1
,
a
l
1
+
1
,
.
.
.
,
a
r
1
−
1
,
a
r
1
}
\{a_{l_1} , a_{{l_1}+1},...,a_{{r_1}-1},a_{{r_1}}\}
{al1,al1+1,...,ar1−1,ar1}和
{
a
l
2
,
a
l
2
+
1
,
.
.
.
,
a
r
2
−
1
,
a
r
2
}
\{a_{l_2} , a_{{l_2}+1},...,a_{{r_2}-1},a_{{r_2}}\}
{al2,al2+1,...,ar2−1,ar2},其中
l
1
≤
r
1
<
l
2
≤
r
2
l_1 ≤ r_1 < l_2 ≤ r_2
l1≤r1<l2≤r2。
两个队伍的人数不必相同,但是需要让队伍内的同学们的力量值之和尽可能相近。请计算出力量值之和差距最小的挑选队伍的方式。
输入格式
输入共两行。
第一行为一个正整数
n
n
n。
第二行为
n
n
n 个正整数
a
i
a_i
ai。
输出格式
输出共一行,一个非负整数,表示两个队伍力量值之和的最小差距。
样例输入
5
10 9 8 12 14
样例输出
1
样例说明
其中一种最优选择方式:
队伍1:
{
a
1
;
a
2
;
a
3
}
\{a_1; a_2; a_3\}
{a1;a2;a3},队伍2:
{
a
4
;
a
5
}
\{a_4; a_5\}
{a4;a5},力量值和分别为
10
+
9
+
8
=
27
10 + 9 + 8 = 27
10+9+8=27,
12
+
14
=
26
12 + 14 = 26
12+14=26,差距为
∣
27
−
26
∣
=
1
|27 − 26| = 1
∣27−26∣=1。
评测用例规模与约定
对于
20
%
20\%
20% 的评测用例,
n
≤
50
n ≤ 50
n≤50
对于
100
%
100\%
100% 的评测用例,
n
≤
1
0
3
,
a
i
≤
1
0
9
n ≤ 10^3,a_i≤10^9
n≤103,ai≤109
思路
前缀和+set
#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
typedef long long ll;
ll n;
ll num[1010] = {0};
ll pre[1010] = {0};
multiset<ll> mset;
int main() {
ios::sync_with_stdio(false);
cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> num[i];
pre[i] = pre[i - 1] + num[i];
}
for (int l = 1; l <= n; l++) {
for (int r = l + 1; r <= n; r++) {
mset.insert(pre[r] - pre[l]);
}
}
ll res = 999999999;
for (int r = 1; r < n; r++) {
for (int l = 0; l < r; l++) {
ll val = pre[r] - pre[l];
auto it = mset.lower_bound(val);
if (it != mset.end()) {
res = min(res, abs(*it - val));
}
if (it != mset.begin()) {
it--;
res = min(res, abs(*it - val));
}
}
for (int i = r + 1; i <= n; i++) {
mset.erase(mset.find(pre[i] - pre[r]));
}
}
cout << res << endl;
return 0;
}