题目:
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a 1,a 2……a n。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。
Input 输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a 1,a 2……a n的值。 Output 输出不同的选择物品的方式的数目。
Sample Input
3
20
20
20
Sample Output
3
思路:
这道题要求的是给出n个物品的重量,要求让他们的重量和为40时,有多少种情况。
刚开始看这道题的时候,觉得用搜索就可以写,从重量为40,物品数为n开始往下找。两种情况,一种是选择这个物品,重量减去a[i], 一种是不选择这个物品,重量不变,继续往下找。
int dfs(int sum,int num)
{
if(sum == 0)
return 1;
if(num == 0)
return 0;
return dfs(sum-a[num],num-1)+dfs(sum,num-1); //取或者不取
}
当然,如果会用dp写也是挺不错的。对于dp大家对于01背包这个还算了解吧,其实这个和它也差不多。在解决dp问题的时候,我们应该从后往前推,找到问题的来源,比如在这道题中,要求的是重量为40时,有多少种情况,那么我们可以利用二维数组dp[i][j], i表示的是重量,j表示物品的个数。 当然dp[i][j]的数值应该等于重量为i,数值为j-1的情况相同。 如果a[j]恰好和重量i相等,那么dp[i][j]就加1,如果a[j]小于i时,那么dp[i][j] += dp[i-a[j]][j-1]。其实这个等式表示的是重量为i,数量为j的情况个数,应该等于其本身的情况再加上重量为 i-a[j],物品个数为j-1的 情况。
for(int i = 1; i <= 40; i++) //表示物品的总重量
{
for(int j = 1; j <= n; j++) //表示前j种物品在重量为i时,有几种情况
{
dp[i][j] = dp[i][j-1]; //重量为i, 前j-1个物品的情况个数
if(i == a[j])
dp[i][j]++; //情况加1
if(i > a[j])
dp[i][j] += dp[i-a[j]][j-1]; //现在的情况数加上 重量为i-a[j]时,j-1个物品的情况数
}
}
我们也可以用一维数组来表示重量,其意义和上面一样。代码如下
for(int i = 1; i <= n; i++)
{
for(int j = 40; j >= 1; j--)
{
if(dp[j] && j+a[i] <= 40)
{
dp[j+a[i]] += dp[j];
}
}
dp[a[i]]++;
}