<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Knapsack |</title><link>https://sichen-wang.github.io/tags/knapsack/</link><atom:link href="https://sichen-wang.github.io/tags/knapsack/index.xml" rel="self" type="application/rss+xml"/><description>Knapsack</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sat, 22 Jun 2024 15:07:48 +0000</lastBuildDate><image><url>https://sichen-wang.github.io/media/icon_hu_9dfcb674aa885f38.png</url><title>Knapsack</title><link>https://sichen-wang.github.io/tags/knapsack/</link></image><item><title>某 0/1 背包 Trick</title><link>https://sichen-wang.github.io/blog/%E6%9F%90-01-%E8%83%8C%E5%8C%85-trick/</link><pubDate>Sat, 22 Jun 2024 15:07:48 +0000</pubDate><guid>https://sichen-wang.github.io/blog/%E6%9F%90-01-%E8%83%8C%E5%8C%85-trick/</guid><description>&lt;p&gt;有 \(n\) 个物品, 第 \(i\) 个重量为 \(a_{i}\), 求重量不大于 \(c\) 的情况下能够装下的最大重量.&lt;/p&gt;
&lt;p&gt;\(n, m = \max\{a_{i}\} \leqslant 2 \times 10^{4}, c \leqslant 10^{9}.\)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;考虑随便贪心出来一组解 \(y\), 设正确答案为 \(x\), 显然 \(0 \leqslant x - y \leqslant m\).&lt;/p&gt;
&lt;p&gt;考虑求最大的 \(k \leqslant c - y\), 满足存在一种方案, 删除一些贪心方案中选择的物品, 放入一些贪心方案中没有选择的物品, 总重量的变化量为 \(k\). \(y + k\) 即为正确答案.&lt;/p&gt;
&lt;p&gt;设数列 \(\{s_{i}\}\) 为贪心方案中未选的, 数列 \(\{t_{i}\}\) 为贪心方案中选择了的数的相反数, 其中选择 \(t_{i}\) 用以刻画从原方案中删除 \(-t_{i}\).&lt;/p&gt;
&lt;p&gt;注意到以下策略：当且仅当 \(k + s_{i} \leqslant m\) 时才考虑是否选择 \(s_{i}\), 当且仅当 \(k \gt 0\) 时, 才考虑是否从 \(\{t_{i}\}\) 中选数. 容易发现, 不论最终答案如何分布, 一定可以用这种策略刻画出来.&lt;/p&gt;
&lt;p&gt;于是我们发现在转移的过程中, \(|k| \leqslant m\) 始终成立. 因此可以导出一个简单的 \(\rm DP\)：设 \(f_{i, j, k} = 0/1\) 表示当前考虑了 \(s_{1 \sim i}\) 和 \(t_{1 \sim j}\) 中的数, 能否表示 \(k\). 据前文所述策略, 转移是显然的, 时间复杂度为 \(\Theta(n^{2} m)\).&lt;/p&gt;
&lt;p&gt;考虑继续优化, 注意到 \(0/1\) 单独占一个状态表示位非常浪费, 于是设 \(f_{i, k} = j\) 表示在考虑 \(s_{1 \sim i}\) 的情况下, 想要表示 \(k\), 至少还需要考虑 \(t_{1 \sim j}\).&lt;/p&gt;
&lt;p&gt;依据前文策略, 考虑两种转移：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若 \(k + a_{i + 1} \leqslant m\), 则考虑是否选择 \(a_{i + 1}\), 转移到 \(f_{i + 1, k + a_{i + 1}}\) 和 \(f_{i + 1, k}\).&lt;/li&gt;
&lt;li&gt;若 \(k \gt 0\), 则考虑从 \(\{t_{i}\}\) 中选择一个数进行转移, 注意到该下标 \(p \in [f_{i, k} + 1, f_{i - 1, k}]\).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关于 \(p\) 的范围, 首先下界是显然的, 因为如果使用 \(t_{f_{i, k}}\) 及之前的数可能会算重. 对于上界, \(f_{i - 1, k}\) 进行同样转移时的下界即为 \(f_{i - 1, k} + 1\), 又因为表示 \(k\) 只需要 \(s_{1 \sim i - 1}\) 中的数即可, 若 \(f_{i, k}\) 进行转移时使用了 \(f_{i - 1, k}\) 转移时使用的同样的 \(\{t_{i}\}\) 中的数, 那么一定是不优的, 故没有必要.&lt;/p&gt;
&lt;p&gt;关于时间复杂度：第一种转移带来的时间复杂度为 \(\Theta(nm)\). 第二种转移中, 对于一个固定的 \(k\), \(p\) 的枚举至多遍历 \(1 \sim |\{t_{i}\}|\) 中每个数至多一遍, 于是该部分时间复杂度也为 \(\Theta(nm)\).&lt;/p&gt;
&lt;p&gt;总时间复杂度为 \(\Theta(nm)\).&lt;/p&gt;</description></item></channel></rss>