#6803. 2026/5/23/CQY课堂笔记(字符串贪心)
2026/5/23/CQY课堂笔记(字符串贪心)
📚 本周编程笔记
📊 一周速览
| 提交 | 35 次 |
| AC | 12 道 |
| 一次通过 | 5 题 |
| 知识点 | 贪心 · 找规律 · 模拟 · 背包DP |
一、贪心:删数问题
核心思想
从左往右扫描,删掉第一个"逆序"位置的数字。求最小删大的,求最大删小的。
代码模板
string s; int k;
cin >> s >> k;
while(k--){
bool removed = false;
for(int i = 0; i < s.size()-1; i++){
if(s[i] > s[i+1]){ // 求最小用 >
s.erase(i, 1);
removed = true;
break;
}
}
if(!removed) s.erase(s.size()-1, 1);
}
while(s[0] == '0') s.erase(0, 1); // 前导零
cout << s;
本周例题
- P1839 删数问题 ✅(一次AC)
二、贪心:拼接最大数
核心思想
用拼接后的结果来比较,决定排序顺序。
代码模板
bool cmp(string a, string b){
return a + b > b + a;
}
sort(s+1, s+1+n, cmp);
string ans;
for(int i = 1; i <= n; i++) ans += s[i];
// 前导零处理
int t = 0;
while(t < ans.size() && ans[t] == '0') t++;
if(t == ans.size()) cout << 0;
else cout << ans.substr(t);
本周例题
- P6787 拼接最大数 ✅(一次AC)
三、找规律
核心思想
观察数据规律,用数学关系直接计算答案。
本周例题
P4640 奇妙周期(调了4次)
if(n % 6 <= 3 && n % 6 != 0) cout << "Expedition";
else cout << "Repair";
易错点
- 找规律题要多试几个数验证猜想
- 边界条件要写清楚(
n%6 != 0不能漏)
四、模拟与计数
核心思想
按题目描述一步步执行,不要想复杂了。
本周例题
P4608 云朵工厂(调了1次)
s = 's' + s; // 加个前缀方便处理
char x = s[1];
int ans = 1;
for(int i = 2; i <= n; i++){
if(s[i] != x){ // 不一样就+1
ans++;
x = s[i];
}
}
cout << ans;
复习要点:统计连续段数量,遇到不同就切换。
P4607 自动扶梯(一次AC)
if(c == 'D'){
ans = t + n;
}else{
if(n <= t) ans = n;
else ans = t + b + n;
}
复习要点:分情况讨论,'D'下楼 vs 'U'上楼逻辑不同。
P4611 乒乓球(调了4次)
if(n % m != 0) cout << 1; // 不能整除就要借
else cout << 0;
复习要点:判断整除,简单题不要想复杂。
P6791 cowsignal(一次AC)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
cin >> s[i][j];
for(int x = (i-1)*k+1; x <= i*k; x++)
for(int y = (j-1)*k+1; y <= j*k; y++)
ans[x][y] = s[i][j];
}
复习要点:图形放大,每个像素复制 k×k 次。坐标映射
(i-1)*k+1到i*k。
五、背包DP
核心思想
用 DP 数组记录"容量为 j 时能获得的最大价值",状态转移 = 选或不选。
01背包模板
int dp[20005];
cin >> m >> n; // m=容量, n=物品数
for(int i = 1; i <= n; i++){
cin >> w[i]; // 重量/代价
for(int j = m; j >= w[i]; j--){ // 倒序!
dp[j] = max(dp[j], dp[j-w[i]] + w[i]);
}
}
cout << m - dp[m]; // 剩余空间
本周例题对比
| 题号 | 类型 | 状态转移 | 输出 |
|---|---|---|---|
| P600 装箱 | 01背包 | dp[j-w]+w |
剩余空间 |
| P582 采药 | dp[j-t]+v |
最大价值 |
P600 装箱问题(调了2次)
for(int i = 1; i <= n; i++)
for(int j = m; j >= a[i]; j--)
dp[j] = max(dp[j], dp[j-a[i]] + a[i]);
cout << m - dp[m];
P582 采药(一次AC)
struct med{ int t, v; } a[110];
int f[110][1010]; // 二维写法
for(int i = 1; i <= m; i++)
for(int j = 0; j <= t; j++)
if(j >= a[i].t)
f[i][j] = max(f[i-1][j-a[i].t]+a[i].v, f[i-1][j]);
else
f[i][j] = f[i-1][j];
cout << f[m][t];
易错点
- 倒序遍历:01背包必须从大到小,否则会重复选
- 二维写法比一维更容易理解,建议先学二维
六、其他
P4612 彩灯(调了6次)
int ans = 0, s = 0;
for(int i = 1; i <= n; i++){
if(a[i] == s + 1){ // 刚好是下一个
s++;
ans++;
}
}
if(ans == 0) cout << -1;
else cout << n - ans;
复习要点:找最长连续递增子序列(从1开始),其他需要调整。
P6789 数列(调了1次)
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++){
swap(b[i], b[j]);
int ans = 0;
for(int k = 1; k <= n; k++)
if(a[k] == b[k]) ans++;
v[ans] = 1;
swap(b[i], b[j]); // 恢复原数组
}
}
复习要点:枚举所有交换,统计相同位置数,去重输出。注意
j从i+1开始。
P4609 分拣包裹(调了1次)
// 把所有候选放一起排序,取前 x+y 个最大的
for(int i = 1; i <= x; i++) s[++t] = h[i];
for(int i = 1; i <= y; i++) s[++t] = l[i];
for(int i = 1; i <= k; i++) s[++t] = z[i];
sort(s+1, s+1+t, cmp);
for(int i = 1; i <= x+y; i++) ans += s[i];
复习要点:多组数据合并排序,取最优的若干个。
七、易错清单
| 错误类型 | 本周出现 | 典型例子 |
|---|---|---|
| 找规律不完整 | 3次 | P4640 边界漏判 |
| 简单题想复杂 | 2次 | P4611 判断题 |
| 背包顺序错 | 1次 | P600 倒序问题 |
| 前导零处理 | 0 | 本周注意到了 ✅ |
八、下周目标
- 找规律多验证:写代码前多算几个数,确保规律正确
- 简单题别纠结:看到整除判断、计数这种,直接写不要怀疑
- 背包记住倒序:01背包
for(j=m; j>=w[i]; j--)