OSDN Git Service

Update backpack.md
authorMosa-Linking <42469018+Mosa-Linking@users.noreply.github.com>
Mon, 20 Aug 2018 05:55:25 +0000 (13:55 +0800)
committerGitHub <noreply@github.com>
Mon, 20 Aug 2018 05:55:25 +0000 (13:55 +0800)
docs/dp/backpack.md

index e69de29..86bccf3 100644 (file)
@@ -0,0 +1,56 @@
+在学习本章前请确认你已经学习了[动态规划部分简介](https://oi-wiki.cf/dp/)\r
+\r
+在具体讲何为"背包dp"前,先来看如下的例题\r
+#### 例题\r
+\r
+[[USACO07DEC]手链Charm Bracelet](https://www.luogu.org/problemnew/show/P2871)\r
+\r
+本题题意可概括为——N物体,放入容量为M的背包,要求使总价值最大\r
+\r
+由于每个物体只有2种情况——取与不取,正如二进制中的0和1——这类问题便被称为"0-1背包问题"\r
+\r
+#### 0-1背包\r
+\r
+例题中已知条件有第i个物体的体积v[i]和价值w[i],背包总容量\r
+\r
+显而易见的是,可以计算总价值的,只有已经放入背包的物体,因此该题中对"是否为最大值"的判断是建立在"已经放入背包之中"的基础之上的\r
+\r
+已知对于一个容量为v1,可以放置第1到第i件物体的背包,其最大总价值很明显等于容量为v1的背包,放有第1到第(i-1)件物体时的最大值(第i件物体不取时)或者是容量为v1-v[i]的背包,放有第1到第(i-1)件物体时的最大值+w[i](第i件物体取时)\r
+\r
+由此可以得出状态转移方程\r
+\r
+* dp[v1][i]=max(dp[v1][i-1],dp[v1-v[i]][i-1]+w[i])\r
+\r
+有了这样的思路,就可以顺利地写出代码了\r
+\r
+```cpp\r
+for (int i=1;i<=v1;i++)\r
+    for (int l=0;l<=v1-i;l++)\r
+        dp[l+i]=max(dp[l]+w[i],dp[l+i]);\r
+```\r
+按照正确的思路,写出了这样的核心代码,然后就可以提交......\r
+\r
+错!\r
+\r
+让我们再回头看一下代码,i表示当前判断的是第i个物体,l则穷举体积,可是注意一个地方——l是从0到v1-v[i]\r
+\r
+这意味着什么呢?举个栗子,可能在体积为(l)处取物体i新的dp值存到体积为(l+v[i])处,而在体积为(l+v[i])处,物体i再次被取\r
+\r
+所以,当以0~v1-v[i]的顺序穷举时,物体实际上可能被加入多遍,这显然与题意不符\r
+\r
+因此为了避免多取,穷举顺序应为v1-v[i]~0\r
+\r
+因此实际核心代码为\r
+```cpp\r
+for (int i=1;i<=v1;i++)\r
+    for (int l=v1-i;l>=0;l--)\r
+        dp[l+i]=max(dp[l]+w[i],dp[l+i]);\r
+```\r
+\r
+[例题代码](https://www.luogu.org/paste/2mb5d46q)\r
+\r
+Ps.事实上,由到大穷举是另一种背包问题的解法,稍后会提到\r
+\r
+#### 完全背包\r
+\r
+#### 多重背包\r