POJ 3093 Margaritas on the River Walk

题目大意:给定一个容量为V(题目里是D)的背包和N(题目里是V)件物品各自的体积,求有多少种方法使得背包再也装不下任何物品。

思路:既然要使背包装不下任何物品,也就是说这时剩下的物品中体积最小的那一件也不能装入背包,所以就可以枚举剩下的最小体积的这件物品来进行动态规划了。

定义dp[i]=装满体积为i的背包的方法数,volumn[i]=第i件物品的体积,sum[i]=前i件物品的体积之和。

现在要枚举剩下的物品中体积最小的那一件了。先将所有物品按体积大小排序。

首先明确,当第i件物品作为剩下的体积最小的物品时,前i-1件物品就都已经被放到背包里面去了。这时,背包剩余体积为V-sum[i-1];由于不能让第i件物品放入背包中,这时需要的是把体积为V-sum[i-1]-volume[i]+1 ~ V-sum[i-1]放满的方法数,(当然V-sum[i-1]-volume[i]+1可能小于0,要处理一下,k = max(V-sum[i-1]-volume[i]+1,0).)也就是dp数组所对应的那几个数字的和。

如果从体积小的物品开始枚举,考虑当第i件物品不能放入背包的情况,此时,前i-1件物品就都已经被放到背包里面去了,那么就需要计算第i+1 ~ n件物品占据体积k ~ V-sum[i-1]的方法数,然后再在总方法数上加上dp数组对应的值。那么,第i件物品就被考虑了i-1次,此时的算法复杂度为O(N^2 * V)。

为了使得每件物品只被放入到背包一次,考虑从体积大的物品开始枚举。当第i件物品不能放入背包中,而前i件物品都放入了背包中,这时,我们把已知的i+1 ~ N件物品占据体积k ~ V-sum[i-1]的方法数加到总的方法数ans上,然后再去取第i件物品做01背包,供考虑下一件物品不能放入背包的情况使用,直到枚举完全部的物品。

如果你要问我,使用同样的方法来顺序枚举可以不可以,那这个很容易否定。因为在逆序枚举的时候,当第i件物品放不下的时候,第i件物品后的物品都被考虑过了,且第i件物品之后的物品也肯定放不下。而顺序枚举的话,第i件物品之前的物品都被考虑过作为当前放不下的最小物品了,第i件物品放不下不意味着前面被考虑过的i-1件物品放不下,这就违背我们当初的假设了,如果这么做了,还有装进背包的物品的方法就也被考虑进去了。

下面贴一下用降序(逆序枚举)做的代码:

View Code
 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 int dp[1010],volume[35],sum[35];
 8 
 9 inline int max(int a,int b)
10 {
11     return a > b ? a : b;
12 }
13 
14 int main()
15 {
16     int ncase,N,V,num;
17     scanf("%d",&ncase);
18     for(num = 1;num <= ncase;num++)
19     {
20         scanf("%d%d",&N,&V);
21         memset(sum,0,sizeof(sum));
22         memset(dp,0,sizeof(dp));
23 
24         dp[0] = 1;
25 
26         int i,j,ans = 0;
27         for(i = 1;i <= N;i++)
28             scanf("%d",&volume[i]);
29 
30         sort(volume + 1,volume + 1 + N);
31 
32         for(i = 1;i <= N;i++)
33             sum[i] = sum[i - 1] + volume[i];
34 
35         for(i = N;i >= 1;i--)
36         {
37             int k = max(0,V - sum[i - 1] - volume[i] + 1);
38 
39             for(j = V - sum[i - 1];j >= k;j--)
40                 ans += dp[j];
41 
42             for(j = V;j >= volume[i];j--)
43                 dp[j] += dp[j - volume[i]];
44         }
45 
46         if(volume[1] > V)    ans = 0;
47 
48         printf("%d %d\n",num,ans);
49     }
50     return 0;
51 }
原文地址:https://www.cnblogs.com/zhexipinnong/p/2772498.html