欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
附上汇总帖:历年CSP-X复赛真题解析 | 汇总
B4104 购物
【题目来源】
洛谷:[B4104 CSP-X2024 山东] 购物 - 洛谷
【题目描述】
双十一,很多人在疯狂地购物。
商家推出了各种各样的优惠活动,吸引顾客购买更多的商品。
某商家推出如下的优惠活动:
该商家共有 \(n\) 件商品,单独购买第 \(i\) 件商品的费用为 \(a_i\)。顾客也可以花费 \(w\) 购买 一张优惠券,一张优惠券最多可兑换 \(m\) 件商品(无需额外付费)。顾客可以购买任意张优惠券;
如果最后商品不足 \(m\) 件,优惠券也可以使用。
求顾客购买完所有 \(n\) 件商品的最小费用。
【输入】
第一行有 \(3\) 个整数 \(n,m,w\)。
第二行有 \(n\) 个整数,第 \(i\) 个为 \(a_i\),表示第 \(i\) 件商品的费用。
【输出】
购买所有商品的最小费用。
【输入样例】
5 2 8
2 7 1 8 4
【输出样例】
15
【算法标签】
《洛谷 B4104 购物》 #山东# #CSP-X小学组# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型
const int N = 200005; // 定义最大数组长度int n, m, w, ans; // n:物品数量,m:分组大小,w:每组最大价值,ans:总价值
int a[N]; // 存储物品价值signed main() {cin >> n >> m >> w; // 输入物品数、分组大小和每组价值上限// 读取物品价值并降序排序for (int i = 1; i <= n; i++) cin >> a[i];sort(a + 1, a + 1 + n, greater<int>());int res = 0; // 当前组累计价值for (int i = 1; i <= n; i++) {res += a[i]; // 将当前物品加入当前组// 每当凑满m个物品或处理完所有物品时if (i % m == 0 || i == n) {ans += min(res, w); // 累加当前组价值(不超过w)res = 0; // 重置当前组累计价值}}cout << ans << endl; // 输出总价值return 0;
}
【运行结果】
5 2 8
2 7 1 8 4
15
B4105 消灭怪兽
【题目来源】
洛谷:[B4105 CSP-X2024 山东] 消灭怪兽 - 洛谷
【题目描述】
怪兽入侵了地球!
为了抵抗入侵,人类设计出了按顺序排列好的 \(n\) 件武器,其中第 \(i\) 件武器的攻击力为 \(a_i\),可以造成 \(a_i\) 的伤害。
武器已经排列好了,因此不能改变顺序。某件武器可以单独攻击,也可以与相邻的武器进行组合攻击。具体来说,每次你可以把相邻的若干个(可以为 \(1\) 个,即不进行组合)连续的武器组合起来进行攻击,则攻击力为这些连续的武器攻击力之和。
来自外星的怪兽拥有无敌护盾,不会受到任何伤害。
但是人类在交战过程中发现怪兽有个致命的弱点:每次当受到 \(k\) 或 \(k\) 的倍数的伤害时,怪兽的无敌护盾就能被打破。
请你帮助人类求出有多少种组合武器的方案,使得造成的伤害能打破怪兽的无敌护盾。
【输入】
第一行两个正整数 \(n,k\) 如题所述;
第二行为 \(n\) 个正整数,其中第 \(i\) 个数 \(a_i\) 表示第 \(i\) 件武器的攻击力。
【输出】
一行一个整数表示答案。
【输入样例】
5 3
1 2 3 4 5
【输出样例】
7
【算法标签】
《洛谷 B4105 消灭怪兽》 #山东# #CSP-X小学组# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型
const int N = 1000005; // 定义最大数组长度int n, k, ans; // n:数组长度,k:模数,ans:结果计数
int a[N], sa[N]; // a:原始数组,sa:前缀和数组
int cnt[N]; // 记录余数出现次数的数组signed main() {cin >> n >> k; // 输入数组长度和模数k// 计算前缀和数组for (int i = 1; i <= n; i++) {cin >> a[i];sa[i] = sa[i-1] + a[i]; // sa[i]表示前i个元素的和}// 统计同余子数组数量for (int i = 0; i <= n; i++) {// 当前前缀和模k的余数int remainder = sa[i] % k;// 之前出现过相同余数的次数即为新增的子数组数量ans += cnt[remainder];// 更新该余数的出现次数cnt[remainder]++;}cout << ans << endl; // 输出满足条件的子数组数量return 0;
}
【运行结果】
5 3
1 2 3 4 5
7
B4106 翻硬币
【题目来源】
洛谷:[B4106 CSP-X2024 山东] 翻硬币 - 洛谷
【题目描述】
\(n\) 枚硬币从左到右依次排成一排,编号依次为 \(1\) 到 \(n\)。硬币的正面朝上用 \(0\) 表示,背面朝上用 \(1\) 表示,一开始所有的硬币都是正面朝上。
现进行 \(m\) 次操作,每次操作是把一个区间内的所有硬币翻过来:原来正面朝上的变为反面朝上,原来反面朝上的变为正面朝上。
第 \(i\) 次操作的区间 \([l_i,r_i]\):表示把从第 \(l_i\) 到第 \(r_i\) 枚之间的所有硬币都翻过来。
求 \(m\) 次操作后所有硬币从左到右依次组成的 \(01\) 数字序列。
【输入】
第一行整数 \(n\) 和整数 \(m\),表示一共有 \(n\) 枚硬币和 \(m\) 次操作。
以下 \(m\) 行,每行两个正数数 l_i,ri,依次表示每次操作区间。
【输出】
m 次操作结束后 n 枚硬币从左到右依次组成的 01 数字序列。
【输入样例、
5 3
2 4
1 3
3 5
【输出样例】
10101
【算法标签】
《洛谷 B4106 翻硬币》 #山东# #CSP-X小学组# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;const int N = 200005; // 定义最大数组长度
int n, m; // n:二进制串长度,m:操作次数
int a[N]; // 差分数组int main() {cin >> n >> m; // 输入二进制串长度和操作次数// 处理每个操作区间for (int i = 1; i <= m; i++) {int l, r;cin >> l >> r; // 输入操作区间a[l] ^= 1; // 区间起始位置取反a[r + 1] ^= 1; // 区间结束位置+1取反(差分思想)}// 计算前缀异或和,得到最终二进制串for (int i = 1; i <= n; i++) {a[i] ^= a[i - 1]; // 当前位与前一位异或}// 输出最终二进制串for (int i = 1; i <= n; i++) {cout << a[i];}cout << endl;return 0;
}
【运行结果】
5 3
2 4
1 3
3 5
10101
B4107 刷题
【题目来源】
洛谷:[B4107 CSP-X2024 山东] 刷题 - 洛谷
【题目描述】
比赛之路多艰,做题方得提升。努力刷题的人在比赛中往往能取得很好的成绩,小红就是这样的人。
为了继续提升自己的编程实力,小红整理了一份刷题题单,并选中了题单中的 \(n\) 道编程题,将它们从 \(1\) 到 \(n\) 编号,计划用 \(m\) 天时间按照题目编号顺序做完所有的题目(一道题目只能在同一天完成,不可以使用多天完成同一道题目)。
在小红的计划中,她完成第 \(i\) 道题目的时间为 \(a_i\)。因为题目有难有易,小红做题时可以找好朋友小明帮忙解题,通过询问小明一道题目的解法,可以省去这个题目的做题时间。当然了,小红做题是为了提升自己,而不是提升小明。因此小红决定一天最多求助小明一次。
本题 \(m\) 天中,小红做题时间最长一天的总耗时定义为 \(T\)(小明帮忙做的题目不计入小红的做题总时间)。请你帮小红求出 \(T\) 的最小值是多少?
【输入】
第一行两个正整数 \(n,m\) 分别表示小红做的题目以及小红刷完这些题目计划所用天数。
第二行 \(n\) 个正整数,分别表示每个题目解题所用时间 \(a_i\)。
【输出】
输出仅一行,\(m\) 天中耗时最长一天的总耗时 \(T\) 的最小值。
【输入样例】
4 2
1 2 3 3
【输出样例】
3
【算法标签】
《洛谷 B4107 刷题》 #山东# #CSP-X小学组# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;const int N = 100005; // 定义最大任务数量
int n, m; // n:任务数量, m:最大分组数
int a[N]; // 存储每个任务的耗时// 检查函数:判断是否可以在每组耗时不超过x的情况下,将任务分成不超过m组
bool check(int x) {int cnt = 0; // 当前分组数int tmp = 0; // 当前组的累计耗时int maxn = 0; // 当前组中的最大任务耗时for (int i = 1; i <= n; i++) {tmp += a[i]; // 将当前任务加入当前组maxn = max(maxn, a[i]); // 更新当前组最大任务耗时// 如果当前组累计耗时超过xif (tmp > x) {cnt++; // 增加分组数tmp -= maxn; // 移除当前组最大任务(贪心策略)// 尝试将后续任务加入当前组(不超过x)int j = i + 1;while (j <= n && tmp + a[j] <= x) {tmp += a[j++]; // 加入后续任务}maxn = 0; // 重置当前组最大任务耗时tmp = 0; // 重置当前组累计耗时i = j - 1; // 更新索引(因为j已经指向下一个未处理任务)}}// 处理最后一组if (tmp > 0) cnt++;// 判断分组数是否满足要求return cnt <= m;
}int main() {cin >> n >> m; // 输入任务数量和最大分组数// 输入每个任务的耗时for (int i = 1; i <= n; i++) {cin >> a[i];}// 二分查找最小可能的最大组耗时int l = 0, r = 1e9; // 初始化左右边界while (l < r) {int mid = (l + r) >> 1; // 取中间值// 检查mid是否满足条件if (check(mid)) {r = mid; // 满足则尝试更小的值} else {l = mid + 1; // 不满足则需要更大的值}}// 输出结果(最小可能的最大组耗时)cout << l << endl;return 0;
}
【运行结果】
4 2
1 2 3 3
3