[信星社] 称重(脑电波波波波)
我的AC代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
freopen("weight.in", "r", stdin);
freopen("weight.out", "w", stdout);
int n, h;
cin >> n >> h;
int l = 0, r = h, ans = 0;
for (int i = 1; i < n; i++) {
int b;
cin >> b;
int nl = max(0, b - r), nr = min(h, b - l);
if (nl <= nr) {
l = nl; r = nr;
} else {
++ans; l = 0; r = h;
}
}
cout << ans;
return 0;
}
我看了题解才写出的代码。
我感觉这题就很脑电波啊——模板题能套、思维题能想,这个题感觉就是根本不知道出题人是不是做梦写出来的。(潜意识深处の呓语?)想出来是红题,想不出是黑题。但我感觉碳基生物都想不出来,就像你不知道第一个生物是怎么出现的一样无厘头。你看看能发现什么经验或规律不?
以下内容由 DeepSeek 生成
你这种“想出来是红题,想不出是黑题”的感觉,其实精准地描述了贪心构造题的典型特征。这题的关键不在于算法模板,而在于一个“决策视角的转换”。我们把它拆开看,你会发现它没有那么“呓语”。
题目在说什么
有 \(n\) 个物品排成一行,每个重量在 \([0, H]\) 内。给你 \(n-1\) 条记录,第 \(i\) 条说“第 \(i\) 个和第 \(i+1\) 个重量之和是 \(b_i\)”。但这些记录可能互相矛盾。你要删掉尽量少的记录,让剩下的记录能同时成立。
题解里给的核心观察是:
如果连续的一段记录都保留,那么每件物品的可行重量构成一个连续区间。
这是整道题唯一的“开关”。一旦想到这句话,后面就全是机械操作了。
为什么你想不到“可行区间”这个视角
因为大多数人拿到题的第一反应是:枚举每个物品的重量,或者枚举删哪些记录。这两条路都是组合爆炸。
而题解的视角是:我不枚举具体值,我维护一个值的范围。
假设当前物品的可行重量是区间 \([L, R]\)。下一条记录说 \(w_i + w_{i+1} = b_i\),那么:
当 \(w_i\) 在 \([L, R]\) 里取遍时,\(w_{i+1}\) 的范围就是 \([b_i - R,\ b_i - L]\)。再和 \([0, H]\) 取交集:
如果 \(L' > R'\),说明这条记录和前面的记录无法同时成立,必须删掉当前这条。
你的代码里就是这两行:
int nl = max(0, b - r), nr = min(h, b - l);
if (nl <= nr) { l = nl; r = nr; }
else { ++ans; l = 0; r = h; }
完全对应。
“删除并重置”为什么是对的
这是贪心正确性的核心,也是“脑电波”感最强的地方。
策略是:从左到右扫,一旦发现矛盾,就删掉当前这条记录,然后把区间重置为 \([0, H]\),从下一个物品重新开始。
直觉上你会担心:万一删前面某一条比删当前这条更优呢?
题解的证明用的是调整法:
任取一个最优方案,考虑第一段从 \(s\) 开始,\(b_t\) 是第一个出现矛盾的位置。原方案在 \(s\) 到 \(t\) 中至少删了一条。我们可以把这段内的删除全部替换成只删 \(b_t\):左侧 \(b_s \dots b_{t-1}\) 全部保留(因为 \(t\) 是第一个矛盾位置,它们一定相容);右侧删除 \(b_t\) 后,物品 \(t+1\) 与左侧断开约束,原方案右侧仍然成立。这样调整后删除数不会增加。
翻译成人话:第一次矛盾的位置,删它永远不亏。因为矛盾是“累积”出来的,你删更早的记录,后面未必更好;而删当前这条,左边全部保留,右边断开重来,不会比任何最优解差。
这就是为什么你的代码可以在 else 分支里直接 ++ans 并重置,不用回头修改前面的决策。
这类题的经验规律
你说得对,这题“模板题能套、思维题能想”,但它既不是纯模板也不是纯直觉。我把它归为“可行域贪心”,这类题有几个可识别的信号:
信号一:约束是“相邻之和”“相邻之差”这类线性关系。
一旦看到 \(w_i + w_{i+1} = b_i\) 或 \(w_{i+1} - w_i = b_i\),并且每个 \(w_i\) 有独立的上界下界,就要立刻想:可行值是不是构成区间? 因为线性约束把区间映射成区间,这是闭区间在加法下的性质。
信号二:问“最少删多少条”而不是“怎么构造”。
“最少删除”往往意味着贪心,而贪心的关键通常是找到“第一次必须做决策的位置”。这题里就是第一个矛盾点。
信号三:数据范围 \(n \le 2\times 10^5\),但值域 \(H\) 可能很大。
如果 \(H\) 很小(比如 \(\le 2000\)),你可以开一个 DP 数组扫值域;但 \(H\) 可以到很大,这就逼着你放弃“枚举值”,转向“维护区间”。题解也说了,\(O(nH)\) 的 DP 能拿 40 分,但满分需要 \(O(n)\) 的区间贪心。
信号四:题目描述里有“记录可能矛盾”“可以删除”这类字眼。
这是在暗示你:不需要让所有约束同时成立,你拥有“拒绝约束”的权力。 有权力拒绝,贪心就往往成立——因为你可以随时“断尾求生”。
你可以怎么训练这种直觉
-
做完一题后,专门问自己:这题的“关键观察”是什么?它是怎么从题目条件里长出来的?
比如这题:\(w_i + w_{i+1} = b_i\) + 每个 \(w_i\) 有界 → 可行区间。把这条逻辑链写下来。 -
积累“映射”直觉:
- 区间 + 线性变换 → 区间
- 区间 + 取交集 → 区间
- 区间为空 → 约束冲突
这类代数直觉在贪心构造题里非常常用。
-
对拍时故意写暴力:
这题的暴力是枚举删哪些记录 + 检查可行性。写一遍暴力,你就能亲眼看到“贪心删当前”和“暴力最优删”的结果总是一样,正确性就变得具体了。
你代码风格还是那么稳:max/min 取边界、++ans 重置区间、freopen 模板,一眼就是你的手笔。这题你“看了题解才写出来”不丢人——贪心构造题的关键观察本来就是竞赛里最稀缺的能力,它靠的是积累“视角”,不是靠聪明。
你想接着拿这题的暴力对拍练一下,还是去找下一道同类型的“可行域贪心”来验证一下这个直觉?

浙公网安备 33010602011771号