感谢 此神仙 在这题上对我的帮助,这里表达对祂衷心的膜拜。
Description
Solution
妙妙题。
暴力: Subtask 1-2
注意到所有物品的重量总和不超过 1010000 1010000 1010000,于是就是个裸的多重背包。
使用单调队列优化的复杂度为 O ( N 4 ) O(N^4) O(N4),常数较大;使用二进制分组优化的复杂度为 O ( N 4 log N ) O(N^4 \log N) O(N4logN),常数较小且好写。具体两个能拿多少分没试过。
简化版
首先思考一个简化版的问题:总重量不需恰好为 m m m,只需不超过 m m m。
不难发现,此时我们可以直接贪心。具体来说,我们预先选上所有重量为负的物品,那么接下来,选中一个重量 V V V 为负的物品等价于将其丢弃,可以使总重量增大 ∣ V ∣ |V| ∣V∣ 并减少一个物品,单价为 − 1 ∣ V ∣ -\frac {1} {|V|} −∣V∣1;选中一个重量 V V V 为正的物品,可以使总重量增大 V V V 并增加一个物品,单价为 1 V \frac 1 V V1。显然的,根据贪心的思想,我们要先尝试选单价大的,再尝试选单价小的,于是我们应先尝试选 1 , 2 , 3 , ⋯ , m 1,2,3,\cdots,m 1,2,3,⋯,m 这些物品,再尝试选 − m , − m + 1 , ⋯ , − 1 -m,-m+1,\cdots,-1 −m,−m+1,⋯,−1 这些物品。至于 0 0 0 的话,我们在接下来的讨论中都默认不存在重量为 0 0 0 的物品,毕竟只需要将最终的答案加上其数量就好了。
于是我们解决了简化版。
正解
回到原题,当要求总重量恰好为 m m m 的时候,我们该怎么办呢?考虑先套用简化版,求出一组贪心解,再对贪心解进行调整。
于是问题转化为,该如何调整,使总重量增长至 m m m 且选出物品的总个数最大。显然的,一次调整,要么将一个物品给弃掉,要么将一个物品给加入,而无论是上述哪种变化,总重量的增值都在区间 [ − m , m ] [-m,m] [−m,m] 中。注意到总重量的总增值(即 m m m 减去贪心解的总重量)也在这段区间中,那么这样一来,调整的次数就可以不超过 2 m + 1 2m+1 2m+1 了。这是因为,我们可以先贪心地排列各次调整的位置,使各处的前缀和均在 [ − m , m ] [-m,m] [−m,m] 中;接着,若存在某两个不同时刻的调整,使它们对应的前缀和相同,那么在这两次调整之间(左开右闭)的调整就都是无意义的,可以将它们一起去掉;不断地执行上述删除操作,直到无法删除为止,调整的次数就被减少到不超过 2 m + 1 2m+1 2m+1 了。
既然调整次数只有 O ( m ) O(m) O(m) 级别,那么各次调整的增量的绝对值之和就是 O ( m 2 ) O(m^2) O(m2) 级别的。所以,我们只需要跑一遍多重背包,就能调整出最优的方案了。
若使用单调队列优化多重背包,那么时间复杂度 O ( m 3 ) O(m^3) O(m3),可以通过本题。实测二进制分组优化也能过,而且跑得还很快。
Code
Summary
启发:
- 遇到这种看似“经典”的题,正解不一定是经典的,要把思维打开,不能太局限。
- 有些时候,无法贪心就要考虑 dp,而无法 dp 也许要考虑贪心。
- 尝试将题目弱化一下,或许能够有利于走向正解。
- 本题的精髓——先贪心构造出一组不超过/不小于的解,接着发现不需要做过多的调整,从而问题"变小",就可以通过 dp 及其他技巧将其调整为一组最优的合法解了。感觉这个思想很值得推广应用。