//ps:学习自存,暂未整理。
知识点
算法:思维,STL,模拟,排序,枚举,查找,递推与递归,贪心,二分,双指针,前缀和、差分与离散化丨常见优化技巧,分治与倍增〔倍增Floyd〕,位运算丨三分,01分数规划
字符串:基础丨kmp,字典树,AC自动机,最小表示法,后缀数组,后缀自动机
数据结构:栈,队列,线性表,链表,二叉树,集合,图的基本应用丨并查集,堆(优先队列),二叉堆与树状数组,线段树〔基础,进阶〕丨平衡树,主席树,RMQ与LCA
搜索:DFS(深度),BFS(广度),记忆化搜索,搜索与搜索剪枝丨蒙特卡洛树搜索
DP:线性DP,背包问题丨数位DP,树形DP,计数DP,区间与环形DP,树与图上的DP,丨状态压缩DP,动态规划的设计与优化(单调队列DP)
数学:基础数学丨辗转相除法,进阶数论,组合数学与计数,概率与统计,基础线性代数,矩阵加速〔矩阵快速幂〕丨散列函数,筛质数,gcd/lcm,快速幂,逆元,扩展欧几里得,费马小定理,更相减损术,博弈论,概率与统计,基础线性代数,矩阵加速(矩阵快速幂)
图论:树〔树的直径〕,最短路(Dijkstra),最小生成树(Kruskal),连通性问题,分层图,类Floyd算法丨二分图,差分约束,连通分量,网络流,欧拉图
计算几何:基础丨点积,叉积,凸包
算法
模拟,排序,枚举,贪心,递推与递归,二分,搜索
前缀和、差分与离散化,常见优化技巧,分治与倍增,字符串,进阶搜索
数据结构
线性表,二叉树,集合,图的基本应用
并查集,二叉堆与树状数组,线段树
线段树的进阶用法
DP
基础DP
线性状态动态规划,区间与环形动态规划,树与图上的动态规划,状态压缩动态规划,动态规划的设计与优化
数学
基础数学
进阶数论,组合数学与计数,概率与统计,基础线性代数
图论
树,最短路,最小生成树,连通性问题
难度
无评定
入门
普及−
普及/提高−
普及+/提高
提高+/省选−
省选/NOI−
NOI/NOI+/CTSC
——————————————————————————————————
Review
L1-1 抓兔子,捣年糕
签到
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
char ans = s[0]; // 直接初始化为字符串的第一个字符,减少一个字符的比较
for (char c : s) ans = min(ans, c); // 使用范围for循环和min函数简化代码
cout << ans << endl;
return 0;
}
L1-2 期初考试
签到
include
include
using namespace std;
int main() {
int n;
cin >> n; // 输入考试门数
double sum = 0; // 存储分数总和,用double以保持精度
for (int i = 0; i < n; ++i) {
int score;
cin >> score; // 输入每门考试的分数
sum += score; // 累加分数
}
cout << fixed << setprecision(4) << sum / n << '\n'; // 输出平均分,保留4位小数
return 0;
}
L1-3 慧音老师的银杏叶
结构体排序
include
include
include
using namespace std;
struct Student {
int id; // 学号
int scores[3]; // 三门课的成绩
int total; // 总分
// 构造函数,用于初始化学生信息
Student(int id, int history, int magic, int virtue) : id(id) {
scores[0] = history;
scores[1] = magic;
scores[2] = virtue;
total = history + magic + virtue;
}
};
// 自定义排序规则
bool compare(const Student &a, const Student &b) {
if (a.total != b.total) return a.total > b.total; // 按总分降序排列
if (a.scores[0] != b.scores[0]) return a.scores[0] > b.scores[0]; // 幻想乡历史学成绩降序
return a.id < b.id; // 学号升序
}
int main() {
int n;
cin >> n;
vector
for (int i = 1; i <= n; ++i) {
int history, magic, virtue;
cin >> history >> magic >> virtue;
students.emplace_back(i, history, magic, virtue);
}
// 使用自定义的比较函数进行排序
sort(students.begin(), students.end(), compare);
// 输出前5名学生的学号和成绩总分
for (int i = 0; i < 5; ++i) {
cout << students[i].id << " " << students[i].total << "\n";
}
return 0;
}
L1-4 反射Master
数学
include
using namespace std;
int main(){
long long n, p, k;
cin >> n >> p >> k;
cout << (k * p) % n << "\n";
return 0;
}
L1-5 幻想乡程序设计大赛
枚举
include
include
include
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int yl, n, yr;
cin >> yl;
cin >> n;
vector
for (int i = 0; i < n; ++i) {
cin >> stops[i];
}
cin >> yr;
// 计算从yl到yr之间的总年份数(包括yr年)
int totalYears = yr - yl + 1;
// 从总年份数中减去停办的年份
for (int stopYear : stops) {
if (stopYear <= yr) {
totalYears--;
}
}
cout << totalYears << endl;
}
return 0;
}
L1-6 惊人的秘密
思维
include
include
include
using namespace std;
// 计算修改为特定模式需要的操作次数
int calculateChanges(const string& answers, char first, char second) {
int changes = 0;
for (size_t i = 0; i < answers.length(); ++i) {
// 当前应该是哪个字符
char expected = (i % 2 == 0) ? first : second;
if (answers[i] != expected) {
changes++;
}
}
return changes;
}
int main() {
int n;
string answers;
cin >> n >> answers;
// 计算修改为BCBCBC...和CBCBCB...需要的操作次数
int changesForBC = calculateChanges(answers, 'B', 'C');
int changesForCB = calculateChanges(answers, 'C', 'B');
// 输出最小操作次数
cout << min(changesForBC, changesForCB) << endl;
return 0;
}
L1-7 贤者的变量替换
哈希映射 + 字符串操作
include
include <unordered_map>
include
using namespace std;
int main() {
unordered_map<string, string> mp;
string s, x;
int n, m;
cin >> n >> m;
while (n--) {
cin >> s >> x;
s = "{" + s + "}";
mp[s] = x;
}
cin.ignore(); // 忽略前一个输入后的换行符
while (m--) {
getline(cin, s);
for (auto &[t, val] : mp) {
size_t pos = 0;// pos 用于记录当前查找的位置。
while ((pos = s.find(t, pos)) != string::npos) {// 在字符串 s 中查找变量名 t。
s.replace(pos, t.size(), val);// 在找到变量名的位置 pos 替换为它的值 val。
pos += val.size(); // 更新 pos 为替换文本之后的位置,以避免替换后的文本被重复查找。
}
}
cout << s << '\n';
}
return 0;
}
L1-8 上海蓬莱
区间合并
include
include
include
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int, int>> dolls(n);
for (int i = 0; i < n; ++i) {
cin >> dolls[i].first >> dolls[i].second;
}
// 按照开始时间对时间段进行排序
sort(dolls.begin(), dolls.end());
int maxWithDoll = 0, maxWithoutDoll = 0;
int start = dolls[0].first, end = dolls[0].second;
for (int i = 1; i < n; ++i) {
if (dolls[i].first <= end) { // 时间段重叠或连续
end = max(end, dolls[i].second);
} else { // 出现了无人浇花的时间段
maxWithoutDoll = max(maxWithoutDoll, dolls[i].first - end);
maxWithDoll = max(maxWithDoll, end - start);
start = dolls[i].first;
end = dolls[i].second;
}
}
maxWithDoll = max(maxWithDoll, end - start); // 更新最后一段时间
cout << maxWithDoll << " " << maxWithoutDoll << endl;
return 0;
}
L2-1 蕾米莉亚巡逻记
双指针
include
include
include <unordered_map>
using namespace std;
int main() {
int n;
cin >> n;
vector
unordered_map<int, int> mp; // 用于记录数字出现的次数
// 读入数据
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
int ans = 0; // 存储最长不重复子数组的长度
// 使用双指针,l为左指针,r为右指针
for(int l = 1, r = 1; r <= n; r++) {
mp[a[r]]++; // 记录当前数字出现的次数
// 如果当前数字出现次数大于1,则移动左指针直到该数字出现次数为1
while(mp[a[r]] > 1) {
mp[a[l]]--; // 左指针对应数字的次数减1
l++; // 左指针右移
}
// 更新最长不重复子数组的长度
ans = max(ans, r - l + 1);
}
cout << ans << '\n';
return 0;
}
L2-2 月光团子
栈
L2-3 你存在的价值
并查集 + 位运算
L2-4 梦想天生
BFS
L3-1 风神少女的祝福
拓扑排序 + 贪心
L3-2 七曜魔法使
最大流 + 质因数分解
L3-3 信仰是为了虚幻之人
树链剖分 + 动态开点线段树
基础
语法
进制转换
第十三届蓝桥杯C++B组 A题 — 九进制转十进制
include
include<math.h>
using namespace std;
int main()
{
cout << 2pow(9,3)+0pow(9,2)+2pow(9,1)+2pow(9,0) << endl;
return 0;
}
蓝桥2406 字母数(简单)
include
using namespace std;
// 判断给定的整数n转换为十六进制表示时,所有位都大于等于10(即A到F)
bool judge(int n) {
while (n) {
int t = n % 16; // 获取n的最低十六进制位
if (t < 10) { // 如果这一位小于10,说明不满足条件
return false;
}
n /= 16; // 去掉n的最低十六进制位
}
return true; // 所有位都满足条件,返回true
}
int main() {
for (int i = 2023; ; i++) { // 从2023开始循环,寻找满足条件的最小整数
if (judge(i)) { // 如果找到满足条件的整数
cout << i; // 输出该整数
return 0; // 结束程序
}
}
}
B3620 x 进制转 10 进制
include // 仅包含必要的头文件
using namespace std;
// 声明全局变量
int x, a[105]; // x表示输入的进制,a数组用于存储转换后的每一位数字
string S; // S用于存储输入的字符串表示的数字
// char转数码函数,将字符转换为对应的整数值
int charToInt(char c) {
if ('0' <= c && c <= '9') return c - '0'; // 处理数字字符
return c - 'A' + 10; // 处理字母字符,转换为10~35的数字
}
int main() {
cin >> x >> S; // 输入进制和字符串
int len = S.size(); // 获取字符串长度
// 逆序处理字符串,将其转换为整数数组
for (int i = len - 1; i >= 0; i--)
a[len - 1 - i] = charToInt(S[i]);
int ans = 0, w = 1; // ans用于存储最终结果,w为权值,初始为1
for (int i = 0; i < len; i++) { // 遍历每一位,计算其对应的十进制值
ans += w * a[i];
w *= x; // 权值随着位数增加而增加
}
cout << ans; // 输出转换后的十进制数
return 0;
}
思维
T419963 Light 的有序排列
校赛 02-B
题目描述:将一个排列切割成若干段,可进行翻转,重新组合变成一个有序排列,统计最少的切割次数。
include
include // 提供abs函数
using namespace std;
const int N = 1e6 + 10; // 定义常量N为数组大小上限
int n, a[N], res; // n为数组长度,a为数组,res为结果
int main() {
// 提高cin和cout效率的设置
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n; // 输入数组长度
for (int i = 1; i <= n; i++) {
cin >> a[i]; // 输入数组元素
}
// 遍历数组,计算相邻元素差的绝对值大于1的情况数量
for (int i = 1; i < n; i++) {
res += (abs(a[i] - a[i + 1]) > 1); // 如果差的绝对值大于1,则累加到结果中
}
cout << res << '\n';
return 0;
}
模拟
基础
第十三届蓝桥杯C++B组 C题 — 刷题统计
include
using namespace std;
int main() {
long long a, b, n; // 使用long long类型存储大整数
cin >> a >> b >> n; // 输入工作日每天消耗量a,周末每天消耗量b,以及总量n
long long weekCost = a * 5 + b * 2; // 计算一周总消耗量
long long ans = 0; // 初始化答案变量
ans += (n / weekCost) * 7; // 先计算总量可以支持完整周的数量,并转换为天数
n %= weekCost; // 计算剩余量
// 逐天减去消耗量,直到剩余量不足以支持一天
for (int i = 1; n > 0 && i <= 7; ++i) {
if (i <= 5) {
n -= a; // 工作日消耗
} else {
n -= b; // 周末消耗
}
ans++; // 每循环一次,表示多支持了一天
}
cout << ans; // 输出最终可以支持的天数
return 0;
}
蓝桥2407 列名
include
using namespace std;
int main() {
int t = 26 + 26 * 26; // 初始值
bool found = false; // 用来标记是否找到符合条件的情况
for (int i = 65; i <= 90 && !found; i++) {
for (int j = 65; j <= 90 && !found; j++) {
for (int k = 65; k <= 90 && !found; k++) {
t++;
if (t == 2022) {
cout << char(i) << char(j) << char(k) << endl; // 打印字符而不是"ijk"
found = true; // 找到后设置found为true,以退出所有循环
}
}
}
}
return 0;
}
P8597 [蓝桥杯 2013 省 B] 翻硬币
include // 引入IO流库
using namespace std;
string a, b;
int main() {
cin >> a >> b; // 输入两个字符串
int l = a.size(), s = 0; // l 为字符串长度,s 为翻转次数
for(int i = 0; i < l; i++) {
if(a[i] != b[i]) { // 当字符不相同时
a[i] = a[i] == 'o' ? '' : 'o'; // 翻转当前字符
a[i + 1] = a[i + 1] == 'o' ? '' : 'o'; // 翻转下一个字符,不检查边界
s++; // 翻转次数加一
}
}
cout << s << endl; // 输出翻转次数
return 0;
}
模拟+枚举
P1003 [NOIP2011 提高组] 铺地毯
普及−
include
using namespace std;
const int MAXN = 10005; // 数组最大长度
// 定义四个数组,分别存储每个矩形的左下角横坐标、纵坐标、宽度和高度
int a[MAXN], b[MAXN], g[MAXN], k[MAXN];
int main() {
int n, x, y; // n为矩形的数量,x和y为查询点的坐标
cin >> n;
for(int i = 0; i < n; i++) {
cin >> a[i] >> b[i] >> g[i] >> k[i];
}
cin >> x >> y;
int ans = -1;
for(int i = n - 1; i >= 0; i--) {
// 从后向前遍历所有矩形,确保找到的是最上层的矩形
// 判断点(x, y)是否位于矩形i内
if(x >= a[i] && y >= b[i] && x <= a[i] + g[i] && y <= b[i] + k[i]) {
ans = i + 1; // 如果是,更新答案为矩形的编号(编号从1开始)
break;
}
}
cout << ans << endl;
return 0;
}
P1008 [NOIP1998 普及组] 三连击
普及−
include
include
using namespace std;
// 检查三个数是否按1:2:3的比例
bool check(int a, int b, int c) {
return 2 * a == b && 3 * a == c;
}
int main()
{ int nums[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // 优化:直接从第一个排列开始,避免了初始排序的开销
sort(nums, nums + 9); // 确保数组是按升序排列的,对于初始定义的数组,这步可以省略
// 生成并遍历1到9的所有排列
do {
// 将排列分成三个三位数
int a = nums[0] * 100 + nums[1] * 10 + nums[2];
int b = nums[3] * 100 + nums[4] * 10 + nums[5];
int c = nums[6] * 100 + nums[7] * 10 + nums[8]; // 检查是否满足1:2:3的比例,如果满足,则输出这三个三位数
if (check(a, b, c)) {
cout << a << " " << b << " " << c << endl;
}
} while (next_permutation(nums, nums + 9)); // 使用next_permutation生成下一个排列
return 0;
}
P1014 [NOIP1999 普及组] Cantor 表
普及−
include
using namespace std;
int main()
{
int n, k = 1;
cin >> n; // 读取输入的数字
while (n > k) {
n = n - k;
k++;
}
if (k % 2 == 0) {
cout << n << "/" << (k + 1 - n);
}
else {
cout << k + 1 - n << "/" << n;
}
return 0;
}
模拟+贪心
P1007 独木桥
普及/提高−
include
include
include
using namespace std;
const int size = 5005;
int a[size];
int main()
{
int L, N;
cin >> L >> N; // 输入桥的长度和人数
if (!N) { // 如果没有人,则最短和最长时间都是0
cout << "0 0" << endl;
return 0;
}
for (int i = 1; i <= N; i++) cin >> a[i]; // 输入每个人的起始位置
sort(a + 1, a + N + 1); // 对人的位置进行排序,以便后续计算
int max_time = 0, min_time = 0;
for (int i = 1; i <= N; i++) {
// 计算最短时间:找到每个人到达桥的两端的最短距离中的最大值
min_time = max(min_time, min(a[i], L + 1 - a[i]));
}
// 计算最长时间:考虑最远端的两个人到达对岸的时间(排序的好处)
max_time = max(L + 1 - a[1], a[N]);
cout << min_time << ' ' << max_time << endl;
return 0;
}
排序
P1177 【模板】排序
普及−
include
include
include // 用于调用sort函数
using namespace std;
int main() {
int N;
cin >> N; // 表示需要排序的数的个数
vector
// 循环读入N个数到向量arr中
for(int i = 0; i < N; ++i) {
cin >> arr[i];
}
sort(arr.begin(), arr.end()); // 从小到大排序
// 使用范围式for循环输出排序后的数组,简化代码
for(int i = 0; i < N; ++i) {
cout << arr[i];
if(i < N - 1) cout << " "; // 数字之间用空格隔开,最后一个数字后不加空格
}
cout << endl; // 换行
return 0;
}
枚举
第十四届蓝桥杯C++B组 A: 日期统计
//还可以字符串 见后
include
include
include // 包含 find 函数
using namespace std;
int main() {
vector
};
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int ans = 0; // 记录符合条件的子序列数量
// 2023年固定部分
vector
// 遍历所有月份和天数组合
for (int month = 1; month <= 12; month++) {
for (int day = 1; day <= daysInMonth[month]; day++) {
// 构造完整日期子序列:YYYYMMDD
vector
yearSeq[0], yearSeq[1], yearSeq[2], yearSeq[3],
month / 10, month % 10, day / 10, day % 10
};
// 在 array 中搜索日期子序列
auto k = array.begin(); // 使用迭代器进行元素遍历
for (int d:dateSeq) {
k = find(k, array.end(), d);
if (k == array.end()) break;
k++; // 移动到下一个元素以继续搜索
}
if (k != array.end()) {
ans++; // 找到日期,增加计数
}
}
}
cout << ans << endl; // 打印找到的符合条件的子序列数量
return 0;
}
第十三届蓝桥杯C++B组 B题 — 顺子日期
include
using namespace std;
// 定义每个月的天数,注意闰年2月为29天
int day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// 检查数组中的某个部分是否形成顺子
bool check(int a[]) {
for (int i = 3; i <= 5; i++) {
// 检查连续三个数字是否形成顺子(注意顺子的定义是递增的,如123, 456)
if (a[i] == a[i + 1] - 1 && a[i + 1] == a[i + 2] - 1) {
return true;
}
}
return false;
}
int main() {
// 初始日期设置为2022年,因为我们只考虑2022年
int a[8] = {2, 0, 2, 2}; // 年份2022固定
int res = 0; // 用于计算满足条件的天数
// 遍历每个月
for (int i = 1; i <= 12; i++) {
// 设置月份,注意十位和个位是分开的
a[4] = i / 10 % 10; // 十位
a[5] = i % 10; // 个位
// 遍历每天
for (int j = 1; j <= day[i]; j++) {
// 设置日期,注意十位和个位是分开的
a[6] = j / 10 % 10; // 十位
a[7] = j % 10; // 个位
// 检查是否形成顺子
if (check(a)) {
res++; // 如果形成顺子,则结果加1
}
}
}
cout << res; // 输出满足条件的天数总数
return 0;
}
蓝桥2409 大乘积
include
using namespace std;
// 定义一个包含30个元素的数组
int a[30] = {99, 22, 51, 63, 72, 61, 20, 88, 40, 21, 63, 30, 11, 18, 99, 12, 93, 16, 7, 53, 64, 9, 28, 84, 34, 96, 52, 82, 51, 77};
int main() {
int res = 0;
// 外层循环遍历数组中的每一个元素
for (int i = 0; i < 30; i++) {
// 内层循环遍历当前元素之后的每一个元素,以形成唯一的元素对
for (int j = i + 1; j < 30; j++) {
if (a[i] * a[j] >= 2022) {
res++;
}
}
}
cout << res << endl;
return 0;
}
T409142 「YAC Round 1」三妖精SAY WA!!!
校赛 01-A
题目描述:求出数组中 小于等于 上界 m 的最大三数之和。
include
using namespace std;
const int N = 110; // 定义数组大小常量
int a[N], n, m, res; // 定义数组、数组大小、目标和、结果变量
int main() {
// 输入数组大小n和目标和m
cin >> n >> m;
// 输入数组元素
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
// 三重循环枚举所有可能的三个数的组合
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
for (int k = j + 1; k <= n; k++) {
int sum = a[i] + a[j] + a[k];
if (sum <= m) {
res = max(res, sum);
}
}
}
}
cout << res << '\n';
return 0;
}
T422762 爱丽丝的 Sweet Magic
校赛 03-A
题目描述:给定一个数组 a,对其中某一个元素 +1,使得 乘积 尽可能大
include
include
include // 提供max函数
using namespace std;
typedef long long ll; // 定义一个长整型别名ll
int main() {
// 提高cin和cout效率的设置
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector
for (int i = 1; i <= n; i++) {
cin >> a[i]; // 读入数组元素
}
ll res = 0; // 初始化结果为0
// 遍历数组,寻找最大乘积
for (int i = 1; i <= n; i++) {
ll prod = a[i] + 1; // 将当前元素加1
// 计算除了当前元素外其他元素的乘积
for (int j = 1; j <= n; j++) {
if (i != j) prod *= a[j];
}
// 更新最大乘积
res = max(res, prod);
}
cout << res << '\n';
}
return 0;
}
B: 01 串的熵
第十四届蓝桥杯C++B组
include
include
include
using namespace std;
const int total = 23333333;
const double H = 11625907.5798;
int main() {
// 由于函数是关于 i 的对称函数,只需搜索前一半的 i 值
for (int i = 0; i <= total / 2; i++) {
double ans = 0;
// 计算当前 i 对应的 ans 值
if (i > 0) { // 避免对 0 取对数
ans -= 1.0 * i * i / total * log2(1.0 * i / total);
}
if (total - i > 0) { // 同样避免对 0 取对数
ans -= 1.0 * (total - i) * (total - i) / total * log2(1.0 * (total - i) / total);
}
// 如果 ans 与 H 的差值小于 1e-4,则找到了所需的 i
if (abs(ans - H) < 1e-4) {
cout << i << endl;
return 0;
}
}
return 0;
}
搜索
DFS(深度优先搜索)
T422770 迷途竹林的月色(模板)
第十四届蓝桥杯C++B组 D: 飞机降落
include
include
using namespace std;
const int N = 10; // 定义常量N,表示任务的最大数量
int n; // n表示任务的数量
bool st[N]; // st数组用于标记任务是否已被访问
bool flag = false; // flag标记是否找到了有效的顺序
int t[N], d[N], l[N]; // t、d、l分别表示任务的开始时间、截止时间和持续时间
// 深度优先搜索函数,u表示当前处理的任务编号,last表示上一个任务完成的时间
void dfs(int u, int last) {
if (flag) return; // 如果已经找到有效顺序,直接返回
if (u == n) { // 如果所有任务都已处理,标记找到有效顺序并返回
flag = true;
return;
}
for (int i = 0; i < n; i++) { // 遍历所有任务
if (!st[i] && t[i] + d[i] >= last) { //如果任务 i 未被访问(st[i] 为 false)且其开始时间加上持续时间不早于上一个任务的完成时间(last),这意味着任务 i 可以紧接着上一个任务执行
st[i] = true; // 标记任务i为已访问
if (t[i] > last) dfs(u + 1, t[i] + l[i]);
//如果任务i的开始时间晚于上一个任务的完成时间,则从任务i的结束时间(考虑了它的延时l[i])作为新的last值继续搜索。
else dfs(u + 1, last + l[i]);
//如果任务i可以立即开始(即它的开始时间不晚于上一个任务的结束时间),则从上一个任务的结束时间加上当前任务的延时l[i]作为新的last值继续搜索。
st[i] = false; // 回溯,取消标记任务i
}
}
}
int main() {
int T; // T表示测试用例的数量
cin >> T;
while (T--) {
cin >> n; // 输入任务的数量
for (int i = 0; i < n; i++) cin >> t[i] >> d[i] >> l[i]; // 输入每个任务的开始时间、盘旋时间和持续时间
fill(st, st + N, false); // 使用fill函数初始化st数组为false
flag = false; // 初始化flag为false
dfs(0, 0); // 从第0个任务和时间0开始深度优先搜索
if (flag) cout << "YES" << endl; // 如果找到有效顺序,输出YES
else cout << "NO" << endl; // 否则输出NO
}
return 0;
}
第十四届蓝桥杯C++B组 F: 岛屿个数
include
include
using namespace std;
// 方向数组,用于表示岛屿上下左右的探索
int deltaOfIsland[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 方向数组,用于表示海洋的8个方向探索
int deltaOfSea[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
int ans = 0; // 存储最终的岛屿数量
// 用于探索岛屿的深度优先搜索函数
void DFS_Island(vector<vector
data[r][c] = 'N'; // 标记当前位置已经访问
for (int i = 0; i < 4; ++i) {
int newR = r + deltaOfIsland[i][0];
int newC = c + deltaOfIsland[i][1];
if (newR >= 0 && newR < m && newC >= 0 && newC < n) {
if (data[newR][newC] == '1') {
DFS_Island(data, newR, newC, m, n); // 递归访问相邻的岛屿
}
}
}
}
// 用于探索海洋的深度优先搜索函数
void DFS_Sea(vector<vector
data[r][c] = 'N'; // 标记当前位置已经访问
for (int i = 0; i < 8; ++i) {
int newR = r + deltaOfSea[i][0];
int newC = c + deltaOfSea[i][1];
if (newR >= 0 && newR < m && newC >= 0 && newC < n) {
if (data[newR][newC] == '1') {
DFS_Island(data, newR, newC, m, n); // 如果遇到岛屿,则进入DFS_Island
++ans; // 岛屿数量加1
}
else if (data[newR][newC] == '0') {
DFS_Sea(data, newR, newC, m, n); // 如果遇到海洋,则继续探索
}
}
}
}
int main() {
int t;
cin >> t;
vector<vector<vector
for (int i = 0; i < t; ++i) {
int m, n;
cin >> m >> n;
vector<vector
for (int r = 1; r <= m; ++r) {
for (int c = 1; c <= n; ++c) {
cin >> data[r][c]; // 读入原始地图数据
}
}
datas.push_back(data);
}
for (int i = 0; i < t; ++i) {
vector<vector<char>> &data = datas[i];
int m = data.size();
int n = data[0].size();
DFS_Sea(data, 0, 0, m, n); // 从(0,0)开始探索整个地图
cout << ans << endl;
ans = 0; // 重置岛屿数量,为下一个测试用例做准备
}
return 0;
}
BFS(广度优先搜索)
小红走矩阵
include
include
include
using namespace std;
// 用来表示方向的数组,分别对应上、下、左、右
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
// 检查新位置是否有效
bool isValid(int x, int y, int n, int m) {
return x >= 0 && x < n && y >= 0 && y < m;
}
int bfs(vector
int n = matrix.size();
int m = matrix[0].size();
// 创建一个同样大小的矩阵来记录是否访问过
vector<vector
// 使用队列来进行BFS
queue<pair<int, pair<int, int>>> q; // 队列中存储步数和坐标
q.push({0, {0, 0}}); // 从左上角开始
visited[0][0] = true; // 标记为已访问
while (!q.empty()) {
auto front = q.front();
q.pop();
int steps = front.first;
int x = front.second.first;
int y = front.second.second;
// 如果到达右下角,返回步数
if (x == n-1 && y == m-1) return steps;
// 探索四个方向
for (int i = 0; i < 4; ++i) {
int newX = x + dx[i];
int newY = y + dy[i];
// 如果新位置有效且未访问过,且字符不同
if (isValid(newX, newY, n, m) &&
!visited[newX][newY] &&
matrix[newX][newY] != matrix[x][y]) {
visited[newX][newY] = true; // 标记为已访问
q.push({steps + 1, {newX, newY}}); // 加入队列
}
}
}
// 如果队列为空,说明无法到达右下角
return -1;
}
int main() {
int n, m;
cin >> n >> m;
vector
for (int i = 0; i < n; ++i) {
cin >> matrix[i];
}
cout << bfs(matrix) << endl;
return 0;
}
贪心
P8637 [蓝桥杯 2016 省 B] 交换瓶子
include // 引入IO流库
using namespace std; // 使用标准命名空间
const int MAXN = 1e6 + 10;
int num, ans = 0;
int a[10010];
int main() {
int n;
cin >> n; // 输入数组长度
for (int i = 1; i <= n; i++) {
cin >> a[i]; // 输入数组元素
}
// 遍历数组,进行位置交换
for (int j = 1; j <= n; j++) {
while (a[j] != j) { // 当前位置不正确时,进行交换
swap(a[j], a[a[j]]);
ans++; // 记录交换次数
}
}
cout << ans << endl; // 输出交换次数
return 0;
}
T409120 「YAC Round 1」夜雀之歌
校赛01-B
题目描述:构造一个长度为n 的 01 字符串,满足 恰好有 p 个不处于字符串两端的 0 两边是1,且输出字典序最小的构造方案。
include
using namespace std;
// 定义快速输入输出流的宏
define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
int main() {
ios; // 应用快速输入输出流优化
int T, n, p;
cin >> T;
while (T--) {
cin >> n >> p;
// 判断是否可以构造满足条件的字符串
if (n < 2 * p + 1) {
cout << -1 << '\n'; // 如果不能构造,则输出-1
continue;
}
string res; // 定义结果字符串
// 构造多余的0
for (int i = 2 * p + 1; i < n; i++) res += "0";
// 构造p个满足条件的"10"
for (int i = 1; i <= p; i++) res += "10";
// 最后再加一个1
res += "1";
cout << res << '\n'; 、
}
return 0;
}
递推与递归
二分
双指针
第十四届蓝桥杯C++B组 G: 子串简写
include // 包含输入输出流库
using namespace std;
int K;
long long ans = 0, c1_sum = 0; // ans用于存储最终答案,c1_sum用于统计到目前为止遇到的c1字符数量
string S;
char c1, c2;
int main() {
cin >> K >> S >> c1 >> c2; // 输入K值,字符串S和字符c1、c2
// 遍历字符串S,从索引K-1开始,确保每次考虑的子串长度至少为K
for (int i = K - 1, j = 0; i < S.length(); i++, j++) {
if (S[j] == c1) c1_sum++; // 如果当前字符是c1,则增加c1的计数
if (S[i] == c2) ans += c1_sum; // 如果当前字符是c2,则将c1_sum加到答案上
}
cout << ans; // 输出最终的答案
return 0;
}
T408863 魔法使的事,能叫偷吗?
校赛01-D
题目描述:在序列中找到一个包含数字 1,2,3,……,m的最小长度的连续区间[l,r] 。若有多组解,输出 l 最小的那 个。(数据保证一定有解)
include
include
using namespace std;
const int N = 1e6 + 10, M = 2010, inf = 0x3f3f3f3f; // 定义常量
int main(){
ios::sync_with_stdio(false); // 禁用stdio同步以加快cin和cout
cin.tie(nullptr); // 解除cin和cout的绑定,允许它们同时进行
cout.tie(nullptr); // 同上
int n, m;
cin >> n >> m; // 输入数组的长度和不同数字的目标数量
vector
for(int i = 1; i <= n; i++) cin >> a[i]; // 输入数组元素
// 双指针法
int l = 1, r = 1; // 初始化左右指针
int diff = 1; // 当前不同数字的数量
int res = inf; // 最小区间的长度,初始化为无穷大
int res_l, res_r; // 记录最小区间的左右端点
cnt[a[1]] = 1; // 初始化第一个数字的计数
// 遍历数组
while(l <= r && r <= n){
if(diff == m){ // 如果当前区间已经包含m个不同的数字
if(res > r - l + 1){ // 如果当前区间更小,则更新答案
res = r - l + 1;
res_l = l, res_r = r;
}
cnt[a[l]]--; // 左指针对应的数字计数减一
if(!cnt[a[l]]) diff--; // 如果该数字计数为0,不同数字数量减一
l++; // 左指针向右移动
}else{ // 如果当前区间不足m个不同的数字
r++; // 右指针向右移动
if(r > n) break; // 如果右指针超出数组范围,则退出循环
if(!cnt[a[r]]) diff++; // 如果新加入的数字是新的不同数字,则不同数字数量加一
cnt[a[r]]++; // 右指针对应的数字计数加一
}
}
// 如果找到了满足条件的区间,则输出区间的左右端点
if(res != inf){
cout << res_l << ' ' << res_r << '\n';
} else {
cout << "-1 -1\n"; // 否则输出-1 -1,表示没有找到满足条件的区间
}
return 0;
}
前缀和、差分与离散化
T408861 BBA
校赛01-C
题目描述:统计字符串中 bba 型 子序列 个数,字符串只包含小写英文字符。
include
include
include
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false); // 禁用stdio同步以加快cin和cout
cin.tie(nullptr); // 解除cin和cout的绑定,允许它们同时进行
cout.tie(nullptr); // 同上
int n;
string s;
cin >> n >> s;
vector<ll> cnt(26, 0); // 使用vector代替数组,初始化26个字母的计数为0
ll res = 0;
// 遍历字符串中的每个字符
for(int i = 0; i < n; i++){
// 对于每个字符,计算与其不同的字符所形成的对数
for(int j = 0; j < 26; j++){
if(j != s[i] - 'a'){
res += cnt[j] * (cnt[j] - 1) / 2; // 计算组合数并累加到结果中
}
}
// 更新当前字符的计数
cnt[s[i] - 'a']++;
}
cout << res << '\n';
return 0;
}
矩阵
include
include
using namespace std;
int main() {
int n, m, x;
cin >> n >> m >> x;
vector<vector
int sum = 0; // 所有元素之和
vector
vector
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> matrix[i][j];
sum += matrix[i][j]; // 计算总和
rowXor[i] ^= matrix[i][j]; // 更新行的异或和
colXor[j] ^= matrix[i][j]; // 更新列的异或和
}
}
if (sum != x) { // 检查总和是否为x
cout << "wrong answer" << endl;
return 0;
}
int xorValue = rowXor[0]; // 假设第一行的异或和为标准
for (int i = 1; i < n; ++i) {
if (rowXor[i] != xorValue) { // 检查每行异或和是否相等
cout << "wrong answer" << endl;
return 0;
}
}
for (int j = 0; j < m; ++j) {
if (colXor[j] != xorValue) { // 检查每列异或和是否相等
cout << "wrong answer" << endl;
return 0;
}
}
cout << "accepted" << endl; // 所有条件都满足
return 0;
}
线性表
二叉树
集合
图的基本应用
基础数学
第十三届蓝桥杯C++B组 D题: 修剪灌木
include
include // 引入以使用max函数
using namespace std;
int main() {
int n;
cin >> n; // 输入n
for (int i = 0; i < n; ++i) {
// 计算并打印i和n-i-1之间的最大值的两倍
cout << max(i, n - i - 1) * 2;
cout << endl;
}
return 0;
}
第十四届蓝桥杯C++B组 C: 冶炼金属
//也可以二分
include
include // 引入算法库,用于max和min函数
using namespace std;
int main() {
int n;
cin >> n;
int ansMin = 0, ansMax = 1e9; // 初始化最小值和最大值,假设最大值不超过10^9
for (int i = 0; i < n; ++i) { // 使用for循环替代while循环,使逻辑更清晰
int a, b;
cin >> a >> b; // 读入每组的a和b值
// 根据给定的逻辑更新ansMin和ansMax(公式)
// ansMin为所有组中计算得到的最大的最小值
// ansMax为所有组中计算得到的最小的最大值
ansMin = max(a / (b + 1) + 1, ansMin);
ansMax = min(a / b, ansMax);
}
// 输出满足条件的最小值和最大值范围
cout << ansMin << " " << ansMax << endl;
return 0;
}
T429116 「YAC Round 6」琪露诺的超级⑨拆分
入门
题目描述
定义飞阶数字为:每一位都是 9 的,且位数为k 的倍数的正整数。给定多次询问,每次询问一个数字
n,判断这个数字是否可以拆成若千个 k 阶数字的和。
include
using namespace std;
typedef long long ll;
// 函数check用于检查n是否能被k阶的最大数整除
bool check(ll k, ll n) {
ll c = 0;
while (k--) c = c * 10 + 9; // 得到最小k阶数字,实际上是k个9组成的数字
return n % c == 0; // 如果n能被c整除,则返回true,否则返回false
}
int main() {
// 加快IO操作
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int q; // q代表测试案例的数量
cin >> q;
while (q--) { // 循环处理所有测试案例
ll k, n; // k为阶数,n为待检查的数
cin >> k >> n;
// 输出结果,如果n能被k阶的最大数整除,则输出"aya",否则输出"baka"
cout << (check(k, n) ? "aya" : "baka") << '\n';
}
return 0;
}
树
DP
P9242 [蓝桥杯 2023 省 B] 接龙数列
Ai的首位数字恰好等于Ai−1的末位数字
include // 包含输入输出流库
include // 包含string类库
include // 包含算法库,提供max函数
using namespace std;
int n, m, i, dp[10];
string a; // 用字符串存储,便于运算
int main() {
ios::sync_with_stdio(false); // 关闭同步,加速cin和cout
cin.tie(nullptr); // 解绑cin和cout,进一步加速输入输出
cin >> n; // 输入数字的数量
for (i = n; i--; ) {
cin >> a; // 输入每个数字
// 更新以数字最后一位为结尾的最长序列的长度
// a[a.size()-1]-'0' 转换字符为数字
// dp[a[0]-'0']+1 尝试将当前数字作为序列的扩展
dp[a[a.size() - 1] - '0'] = max(dp[a[a.size() - 1] - '0'], dp[a[0] - '0'] + 1);
}
for (i = 0; i <= 9; i++) m = max(m, dp[i]); // 取最大值,即找到最长的由数字构成的序列
cout << n - m; // 输出需要移除的最小数字数量
return 0; // 返回0表示程序正常结束
}
DP最短路(Dijkstra)
T409013 时间冻结
校赛 01-E
题目描述:N 个点,M 条无向边,可以最多 选择 K 条边将其长度缩减为原先长度的一半,求1到 N 的最短距离(边的长度一定是偶数,所以不用担心出现浮点数情况)。
include
include
include
include
using namespace std;
const int N = 60, M = 2010, inf = 0x3f3f3f3f; // 定义最大节点数、最大边数和无穷大表示的常量
// 链式前向星存储图的结构
int h[N], e[M], ne[M], w[M], idx;
// 添加边的函数,a和b是边的两个节点,c是边的权重
void add(int a, int b, int c) {
e[++idx] = b; // 边的目标节点
ne[idx] = h[a]; // 当前边的下一条边
w[idx] = c; // 边的权重
h[a] = idx; // 节点a的第一条边
}
int n, m, K;
int dist[N][N]; // dist[v][cnt] 表示使用了cnt张符卡状态下1到v的最短距离
bool st[N][N]; // st[v][cnt] 表示状态(v, cnt)是否已经在最短路径树中
// 定义节点结构体用于存储优先队列中的信息
struct Node {
int d, u, cnt;
// 重载小于运算符,便于优先队列进行比较,优先队列默认是大根堆,所以这里用大于号
bool operator < (const Node &W) const {
return d > W.d;
}
};
// 堆优化的dijkstra算法
void dijkstra() {
memset(dist, 0x3f, sizeof dist); // 初始化所有距离为无穷大
dist[1][0] = 0; // 起点到自己的距离为0
priority_queue
q.push({0, 1, 0}); // 将起点加入优先队列
while (q.size()) {
Node t = q.top(); // 取出队列顶部元素
q.pop(); // 弹出队列顶部元素
int d = t.d, u = t.u, cnt = t.cnt;
if (st[u][cnt]) continue; // 如果该状态已经处理过,跳过
st[u][cnt] = true; // 标记状态为已处理
for (int i = h[u]; i; i = ne[i]) {
int v = e[i], c = w[i];
// 不使用符卡的情况
if (dist[v][cnt] > d + c) {
dist[v][cnt] = d + c;
q.push({dist[v][cnt], v, cnt});
}
// 使用符卡的情况
if (cnt < K && dist[v][cnt + 1] > d + (c >> 1)) {
dist[v][cnt + 1] = d + (c >> 1);
q.push({dist[v][cnt + 1], v, cnt + 1});
}
}
}
}
int main() {
ios::sync_with_stdio(false); // 禁用同步流,提高cin和cout效率
cin.tie(0); // 解绑cin和cout的绑定
cout.tie(0); // 解绑cout和cerr的绑定
cin >> n >> m >> K; // 输入节点数、边数和符卡数
while (m--) {
int u, v, c;
cin >> u >> v >> c; // 输入边的信息
add(u, v, c); // 添加边到图中
add(v, u, c); // 无向图,需要添加两条边
}
dijkstra(); // 运行dijkstra算法
int res = inf; // 初始化结果为无穷大
// 遍历所有可能的符卡使用次数,找到最短距离
for (int i = 0; i <= K; i++) {
res = min(res, dist[n][i]);
}
cout << res << endl;
return 0;
}
最小生成树
连通性问题
前缀和、差分与离散化,常见优化技巧,分治与倍增
字符串
基础
P1000 超级玛丽游戏
入门
include
using namespace std;
int main()
{
cout<<R"( ******** ************ ####....#. #..###.....##.... ###.......###### ### ### ........... #...# #...# ######### #.#.# #.#.# ########## #.#.# #.#.# ...#..###.... #...# #...# ....******##..... ### ### .... *****.... #### #### ###### ###### ############################################################## #...#......#.##...#......#.##...#......#.##------------------# ###########################################------------------# #..#....#....##..#....#....##..#....#....##################### ########################################## #----------# #.....#......##.....#......##.....#......# #----------# ########################################## #----------# #.#..#....#..##.#..#....#..##.#..#....#..# #----------# ########################################## ############ )"<< endl;
}
A: 日期统计
第十四届蓝桥杯C++B组
include
include
using namespace std;
const int NUMBERS[100] = {
5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,
5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,
2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3,
8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6,
1, 4, 0, 1, 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
};
const int DAYS[13] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int main() {
int ans = 0; // 用于统计符合条件的日期数量
// 遍历每个月的每一天
for (int month = 1; month <= 12; month++) {
for (int day = 1; day <= DAYS[month]; day++) {
string date = "2023"; // 初始化日期字符串,年份固定为2023
// 如果月份是个位数,前面补0
date += (month < 10) ? "0" : "";
date += to_string(month); // 添加月份
// 如果日期是个位数,前面补0
date += (day < 10) ? "0" : "";
date += to_string(day); // 添加日期
int k = 0; // 用于记录匹配的数字数量
// 遍历NUMBERS数组,查找与日期字符串匹配的数字序列
for (int l = 0; l < 100 && k < 8; l++) {
if (NUMBERS[l] == date[k] - '0') k++; //检查NUMBERS数组当前的元素是否与date字符串中当前位置的字符匹配。由于date[k]是一个字符,所以需要通过减去'0'来获取其对应的整数值。
}
// 如果匹配的数字数量达到8或以上,计数加1
if (k >= 8) ans++;
}
}
cout << ans << endl; // 输出符合条件的日期数量
return 0;
}
字符串+模拟
T419870 辉夜的最强密码
校赛 02-A
输入多个字符串,判断每个字符串是否满足指定条件。满足条件输出“True ”;否则输出“False ”。
include
include
include
include // 提供islower, isupper, isdigit等函数
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
string s;
cin >> s;
int n = static_cast
// 条件1:如果字符串长度小于7,直接输出False
if (n < 7) {
cout << "False\n";
continue;
}
// 条件2:检查是否包含小写字母、大写字母和数字
vector
for (auto &ch : s) {
if (islower(ch)) flag[0] = true;
else if (isupper(ch)) flag[1] = true;
else if (isdigit(ch)) flag[2] = true;
}
if (!flag[0] || !flag[1] || !flag[2]) {
cout << "False\n";
continue;
}
// 条件3:检查是否存在连续的数字
bool ok = true;
for (int i = 0; i < n - 1; i++) {
if (isdigit(s[i]) && isdigit(s[i + 1])) {
ok = false;
break;
}
}
cout << (ok ? "True" : "False") << '\n';
}
return 0;
}
字符串+高精度
P1601 A+B Problem(高精)
普及−
include
include
include // 用于reverse函数
using namespace std;
// 高精度加法函数,接受两个字符串形式的非负整数a和b,返回它们的和 string highPrecisionAdd(string a, string b) {
string result; // 存储最终结果
reverse(a.begin(), a.end()); // 将字符串a反转,以便从最低位开始相加
reverse(b.begin(), b.end()); // 将字符串b反转
int carry = 0; // 进位
for(size_t i = 0;
i < max(a.size(), b.size()) || carry; ++i) {
if(i < a.size()) carry += a[i] - '0'; // 如果a的当前位存在,则加到carry上
if(i < b.size()) carry += b[i] - '0'; // 如果b的当前位存在,则加到carry上
result.push_back(carry % 10 + '0'); // 计算当前位的结果,并转换为字符后加到result
carry /= 10; // 计算新的进位
}
reverse(result.begin(), result.end()); // 将结果反转回正确的顺序
return result; // 返回结果字符串 }
int main()
{
string a, b;
cin >> a >> b; // 分两行输入两个非负整数a和b
cout << highPrecisionAdd(a, b) << endl;
return 0;
}
进阶搜索
并查集
P3367 【模板】并查集
普及/提高−
include
using namespace std;
const int MAXN = 10010; // 定义最大的节点数
int f[MAXN]; // f[i] 表示节点i的集合名(或父节点)
// 查找并返回元素k的根节点,同时进行路径压缩 int find(int k) {
if (f[k] != k) { //检查当前元素k是否是自己的父节点。
//如果不是,说明k不是代表元素,需要递归地向上查找。
f[k] = find(f[k]); // 路径压缩
}
return f[k];
}
int main() {
int n, m, p1, p2, p3;
cin >> n >> m; // 输入节点总数和操作总数
// 初始化,每个节点的父节点设置为自己
for (int i = 1; i <= n; i++) {
f[i] = i;
}
// 处理m个操作
for (int i = 0; i < m; i++) {
cin >> p1 >> p2 >> p3; // 输入操作类型和操作涉及的节点
if (p1 == 1) { // 如果是类型1的操作,表示p3打赢了p2,将p2的根节点父亲设为p3的根节点
f[find(p2)] = find(p3);
} else {
// 如果是类型2的操作,查询p2和p3是否属于同一个集合
if (find(p2) == find(p3)) {
cout << "Y\n"; // 是一伙的
} else {
cout << "N\n"; // 不是一伙的
}
}
}
return 0;
}
二叉堆与树状数组
线段树
线段树的进阶用法
线性状态动态规划
区间与环形动态规划
树与图上的动态规划
状态压缩动态规划
动态规划的设计与优化
进阶数论
组合数学与计数
概率与统计
基础线性代数
未分类