01 背包问题
有n个重量和价值分别为wi和vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中总价值总和的最大值。
这是被称为背包问题的一个著名的问题。01 背包是背包问题的其中一种,对于任意一个物品,可以选择0个(不选)和1个。
dp[i+1][j]表示从0到i这i+1个物品中选出总重量不超过j的物品时总价值的最大值。
我们可以得出如下的递推关系式
首先 dp[0][j]=0
dp[i+1][j]=dp[i][j] (j<w[i])
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]) (其他)
void solve() { for(int i=0;i<n;i++) for(int j=0;j<=W;j++) { if(j<W[i]) dp[i+1][j]=dp[i][j]; else dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]); } printf("%d ",dp[n][W]); }
其实我们还可以讲两个数组滚动使用来实现重复使用。例如
dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])
这一递推式中,dp[i+1]计算时只需要dp[i]和dp[i+1],所以可以结合奇偶性写成如下形式:
int dp[2][maxw]; void solve() { for(int i=0;i<n;i++) for(int j=0;j<=W;j++) { if(j<w[i]) dp[(i+1)&1][j]=dp[i&1][j]; else dp[(i+1)&1][j]=max(dp[i&1][j],dp[(i+1)&1][j-w[i]]+v[i]); } printf("%d ",dp[n&1][W]); }
当n很大时,就可以节省很多空间。
还可以这么写,只用一维数组,不过我觉得没上面那个好理解,不具有通适性。
void solve() { for(int i=0;i<n;i++) for(int j=W;j>=w[i];j--) { dp[j]=max(dp[j],dp[j-w[i]]+v[i]); } printf("%d ",dp[W]); }