题目描述
给定一个背包,它的承重是c。给定若干物品,每个物品的重量用w[i]来表示(表示第i个物品的重量),每个物品的价值用v[i]来表示(表示第i个物品的价值)。现在要求,从给定的物品中选择若干物品放入背包,要求重量之和不能大于背包的承重c,且价值最大。
- 注意:每个物品的状态只能是装入背包或者是不装入背包,且只能装入一次。
分析
先给个短例子,假设背包承重为6,可选择的物品共有种,重量依次为【1,3,5】,每个物品的价值依次是【5,6,10】。用dp[i][j]来表示,当可选物品为i,i+1,…,3时,背包承重为j时所选择物品的最优值。dp[1][6]即为所要求的值。
我们开始进行选择,假设我选择了第一件物品放入背包,那么这件物品就要从可选择物品中去掉,背包的承重减一即c=6-1=5,所获得的价值是5,dp[1][6]=dp[2][5]+5;接下来,我们就要从剩余的物品中选择,即求dp[2][5]。但是我们如果不选择第一件物品,背包得承重不会发生变化,那么我们要求的就是dp[2][6],即dp[1][6] = dp[2][6]。但是我们怎么知道选择哪一种呢?要进行比较取两种中结果更大的一个dp[1][6]=max{dp[2][6],dp[2][5]+5}。
可见,我们要求的母问题,可以用它的子问题表示,且子问题和母问题是同一类问题。这种问题具有最优子结构,且母问题的解依赖子问题的解。于是我们想到了用动态规划来求解。动态规划最重要的就是找到问题的转移方程。比如上面的方程dp[i][j] = max{dp[i+1][j],dp[i+1][j-w[i]]+v[i]}就是问题的转移方程。有了转移方程还不够,我们还得确定问题的边界,边界是指当子问题到达一定程度之后,就能直接得到答案,而不需要继续划分子问题。比如dp[3][0]=0,这时,可选的物品为第三个物品,但是此时背包承重为0,这时就不能将物品放入背包中,dp[3][6],此时背包承重为6,我们就可以将物品放入背包。
- 有了边界有了状态转移方程,我们就可以进行编程了。
状态转移方程:dp[i][j] = max{dp[i+1][j],dp[i+1][j-w[i]]+v[i]}
public static int maxValue(int[] v,int[] w,int c,int n) {
int dp[][]= new int[n+1][c+1];
int crisis = min(c,w[n]);
//确定边界值
for(int j=0;j<crisis;j++) {
dp[n][j] = 0;
}
for(int j=w[n];j<=c;j++) {
dp[n][j] = v[n];
}
//动态规划
for(int i=n-1;i>=1;i--) {
crisis = min(c,w[i]);
for(int j=0;j<crisis;j++) {
dp[i][j] = dp[i+1][j];//当前背包容量小于当第i个物品的重量
}
for(int j=w[i];j<=c;j++) {
dp[i][j] = max(dp[i+1][j],dp[i+1][j-w[i]]+v[i]);//当前背包容量大于或等于第i个物品的重量
}
}
return dp[1][c];
}