POJ 3093Margaritas on the River Walk 背包DP

题目链接:http://poj.org/problem?id=3093

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

Sample Input

2
6 25
8 9 8 7 16 5
30 250
1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30

Sample Output

1 15
2 16509438

假设所有物品按照从小到大排序,枚举剩下物品的最小体积,则前面的所有物品都被放入背包,当前的背包空余为v-sum[i],而为了挤占掉所枚举的物品的空间,那么我们就需要最小体积为v-sum[i]-a[i]+1的物品,最大为v-sum[i]的物品。我们将这个范围内的物品体积的方案数直接加到总的ans中就行了,接下来就是物品体积方案数的转移,dp[j]表示空间为j的可以又几个方向转移过来。那么dp[j]=d[j]+dp[j-a[i]]。

以下是AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mac=1e3+10;

int dp[mac],a[mac];

bool cmp(int x,int y){return x>y;}

int main()
{
    int t,n,m,test=0;
    scanf ("%d",&t);
    while (t--){
        int tot=0;
        scanf ("%d%d",&n,&m);
        for (int i=1; i<=n; i++)
            scanf ("%d",&a[i]),tot+=a[i];
        sort(a+1,a+1+n,cmp);
        test++;
        if (a[n]>m) {printf("%d 0
",test);continue;}
        memset(dp,0,sizeof dp);
        dp[0]=1;
        int ans=0;
        for (int i=1; i<=n; i++){//枚举剩下的最小a[i]
            tot-=a[i];//tot的当前值
            int st=max(0,m-tot-a[i]+1);//m-tot,剩余空间,不让a[i]入包所需要占据的最小空间
            for (int j=st; j<=m-tot; j++)//占据a[i]的空间
                ans+=dp[j];
            for (int j=m; j>=a[i]; j--)//空间为j的大小可以由几个方向转移过来
                dp[j]+=dp[j-a[i]];
        }
        printf("%d %d
",test,ans);
    }
    return 0;
}
路漫漫兮
原文地址:https://www.cnblogs.com/lonely-wind-/p/13232732.html