问题描述:
给出N个物品,每个物品的体积为w[i],这些物品可以使多大的背包完全装满?
分析:
用dp[v]表示容量为i的背包可以被装满。 0 <= v <= sum(w[i])
在循环开始前,初始化dp[0] = true,dp[i] = false (i>0)。
第一次循环,表示只有一件物品可选,那么下标为w[0]的背包被置为true。
第二次循环,表示前两件物品可选。那么在所有置为true的下标基础上,加上一个w[1],依然为true。
dp[ j ] == true => dp[ j + w[i] ] = true
但是如果求解时,j从小到大求的话,就会出现问题。
比如 j = 0时, dp[ w[0] ]被置为true。j不断增大,当j = w[0]时,dp[ w[0] + w[0] ]也被置为true。
实际上,物品0只有一件,这样的情况是取不到的。
怎样避免?自然是用逆序求值了。
dp[ j ] = true 仅当 dp[ j - w[i] ] == true。
上代码:
int main() { int a[6] = { 8,3,12,7,9,7 }; int n = 6; int i,j,sum = 46; int dp[47] = {0}; dp[0] = 1; for(i = 0;i < n; i++) { for(j=sum;j>=a[i];j--) { dp[j] |= dp[ j - a[i] ]; } } return 0; }
分析一下循环过程,对于理解用一维数组解背包问题的思维方式非常有帮助。
循环开始前,初始化dp[0] = 1,其他都是0。
第一次循环结束后,下标为a[0]的dp被置为1。
第二次循环后,所有true的下标加上a[1]都变成true。
此后的每次循环,都是在所有合法和的基础上,加上一个a[i]。
最终:
- dp 0x0100fa0c int [47]
[0] 1 int
[1] 0 int
[2] 0 int
[3] 1 int
[4] 0 int
[5] 0 int
[6] 0 int
[7] 1 int
[8] 1 int
[9] 1 int
[10] 1 int
[11] 1 int
[12] 1 int
[13] 0 int
[14] 1 int
[15] 1 int
[16] 1 int
[17] 1 int
[18] 1 int
[19] 1 int
[20] 1 int
[21] 1 int
[22] 1 int
[23] 1 int
[24] 1 int
[25] 1 int
[26] 1 int
[27] 1 int
[28] 1 int
[29] 1 int
[30] 1 int
[31] 1 int
[32] 1 int
[33] 0 int
[34] 1 int
[35] 1 int
[36] 1 int
[37] 1 int
[38] 1 int
[39] 1 int
[40] 0 int
[41] 0 int
[42] 0 int
[43] 0 int
[44] 0 int
[45] 0 int
[46] 0 int