背包问题第二种形式的递推方程
Ⅰ.第一种递推方程
-
背包问题是动态规划问题的经典问题,在我之前的博客里已经讨论过。正常的递推方程是这样的。
F k ( y ) = m a x { F k − 1 ( y ) , F k ( y − v k ) + w k } F_k(y) = max\{F_{k-1}(y),F_k(y-v_k)+w_k\} Fk(y)=max{Fk−1(y),Fk(y−vk)+wk}
-
其主要思想是这样的,第k种物品选不选,如果不选的话,那么就是 F k − 1 ( y ) F_{k-1}(y) Fk−1(y)。如果选的话,就是 F k ( y − v k ) + w k F_k(y-v_k)+w_k Fk(y−vk)+wk,我们注意到这时F的下标仍然是k,因为在取得最大价值的解法里第k种物品可能被选择了多次。如果变成了 F k − 1 ( y − v k ) + w k F_{k-1}(y-v_k)+w_k Fk−1(y−vk)+wk,那么说明第k种物品只能选择一次,变成01背包问题了。
Ⅱ.第二种递推方程
-
在学习算法与设计4.3对贪心法得不到最优解情况的处理这一节中,我看到了一个找零钱问题,大体是这样一个问题:有n种不同的硬币,它们的币值各不相同,第一个硬币的币值是1。现在需要用这些硬币付款的总数是Y,让我们求如何选择硬币使得付钱的硬币最轻。
-
我们看到这和背包问题非常类似。找两个规模参数,一个选择前k个硬币,一个是总钱数y。所以我写下了背包问题相同的递归方程,也就是上文写的。但是我惊奇的发现书上的递归方程不太一样。
F k + 1 = m i n 0 ⩽ x k + 1 ⩽ ⌊ y v k + 1 ⌋ { F k ( y − v k + 1 x k + 1 ) + w k + 1 x k + 1 } F_{k+1}=\underset {0\leqslant x_{k+1} \leqslant \lfloor \frac{y}{v_{k+1}} \rfloor}{min} \{F_k({y-v_{k+1}x_{k+1})+w_{k+1}x_{k+1}}\} Fk+1=0⩽xk+1⩽⌊vk+1y⌋min{Fk(y−vk+1xk+1)+wk+1xk+1}
-
其中v表示硬币的币值,w表示某硬币的个数,x表示选择某硬币的个数
-
思考过后,我发现这是背包问题的第二种形式的递推方程,和原始的递推方程等价。
-
于是我把背包问题的代码按照这个递推方程改了一下,得到了和之前一样的结果。
//背包问题 旅行者有一个背包,可以放入背包的物品有n种,物品j的重量和价值分别是w[j]和v[j].如果背包的最大重量限制是b,怎么样选择放入背包的物品使得背包的价值最大? //输入 每个物品的体积和价值 和背包限制b //输出 最大价值和具体每个物品如何选取 #include <iostream> #include <algorithm> using namespace std; int main() { int w[5] = {0, 2, 3, 4, 7}; //每个物品的重量w[0]弃用 int v[5] = {0, 1, 3, 5, 9}; //每个物品的价值v[0]弃用 int b = 10; //背包限制 int F[5][11] = {0}; //F[k][y]表示在选择前k个物品,并且背包限重y时,取得的最大收益 int number[5][11] = {0}; //number[k][y]表示当背包限重为y时,选择k这个物品的个数 for (int i = 1; i <= 10; i++) //当只取第一种物品的时候,用当前钱购买尽可能多的数量,便能取得最大收益 { number[1][i] = i / w[1]; //选择第一种物品的个数 F[1][i] = i / w[1] * v[1]; } for (int i = 2; i <= 4; i++) //枚举选择前2个物品..前3个..前4个 { for (int j = 1; j <= 10; j++) //枚举背包大小 从1到10 { for (int x = 0; x <= j / w[i]; x++) //枚举选择第k个物品有几个 { int tmp; if (j - x * w[i] >= 0) //如果能够放下x个物品i tmp = F[i - 1][j - x * w[i]] + v[i] * x; //代表第k个物品选x个,剩下的空间从前k-1个物品中选择 else tmp = 0; if (tmp > F[i][j]) { number[i][j] = x; F[i][j] = tmp; } } } } cout << "最大收益:" << F[4][10] << endl; int space = 10; for (int i = 4; i > 0; i--) { cout << "第四件物品:" << number[i][space] << endl; space -= number[i][space] * w[i]; } return 0; }
-
运行结果