从斐波那契到01背包 - 我理解的DP
01背包问题是动态规划的经典入门题目,为了更好的总结与检验,我决定写一篇博文来输出自己的理解。
斐波那契数列的问题早在大一的时候就已经是简单的递归题了,后来大家也发现了直接用数组,从3开始递推就好。
问题就是,递归到底与数组递推有什么关系?
为了搞清楚这个问题,我将Fib的递归函数写下:
int fib (int n) {
if (n == 1 || n == 2) return 1;
return fib(n-1)+fib(n-2);
}
然后设n为5,来模拟fib函数的处理。
fib(5) = fib(4)+fib(3) = [fib(3)+fib(2)] + [fib(2)+fib(1)]
发现,fib(3)这个函数需要执行两次。可以预见,若问题规模变大,将会有大量的重复计算某个fib(i)。所以,正如在许多地方上看到的,我们将每个函数的计算结果保存在数组中。变为记忆化搜索,若有记忆直接使用,否则就计算出来再记下来。
这使得Fib问题最后的处理方式为:
int a[100];
a[1]=a[2]=1;
for(int i = 3; i < 100; i++) a[i] = a[i-1]+a[i-2];
所以忽然想到刚刚问题的答案:
当有重复的子问题需要解决时,我们可以使用数组保存每个子问题的结果来推出父问题的结果,这就是我对DP的理解。
来看01背包问题。
01背包问题
Problem Description
给定n种物品和1个背包,物品i的重量是wi,其价值为vi,背包的容量为C。要求选择装入背包的物品,使得装入背包中物品的总价值最大。
Input
每组测试数据包含3行,第1行为n和c,表示有n(0<=n<=400)个物品且背包容量为c (c<=1500),第二行为这n个物品的重量wi(1<=wi<=1000),第三行为这n个物品的价值vi。背包容量和物品重量都为整数。
Output
输出装入背包的最大总价值,每个答案一行。
Sample Input
5 10
2 2 6 5 4
6 3 5 4 6
5 10
3 1 5 9 3
6 6 2 13 2
Sample Output
15
19
原题地址:usx-1006
思考这个问题,都会发现其实是求在总重量小于c的约束条件下,对n件物品装与不装的合法组合的最优解。
再考虑如果将第1件物品(重量w,价值v)装入,那么问题结果就成为了总重量小于c-w的约束条件下,对n-1件物品装与不装的合法组合的最优解,再加上v。
忽然发现存在子问题,那么不妨我们将每个问题的结果都保存在数组dp中。
设dp[i]为对前i件物品的最优解。
那么对于每件物品j(Wj 即前i件物品的最优解就是不将j装进去,与将j装进去这两种解决方案的最优解。 循环询问每件物品装或不装进去的最优解就可以了。 最终解法见下方代码。dp[i] = max(dp[i], dp[i - w[j] + v[j]);
Code
#include