POJ 3093 Margaritas on the River Walk 0-1背包好题

给n个物品,容量为m的包,要求这样的方案数:选中n个物品放入背包后,剩下的物品中,任意一个都放不进去。

 

我们先将物品按容量从大到小排序。

考虑以下2种情况:

1. 考虑一种特殊情况,一个都放不进去,就是剩下物品中体积最小的那个(体积为w[1])都放不进去,这个情况就会无解,答案为0;

2. 一般情况:从小到大枚举不放入背包的体积最小的,那么此时比它小的物品都应该放入背包中,此时背包装载容量应为[m-w[u]+1,m]  (1<=u<=n)

我们可以对剩下的u+1到n的物品做01背包,然后将可行的方案统计出来,这样,第u件物品考虑了u-1次,总复杂度为O(n^2*m);

优化:

n较大的话会超时,我们可以反过来做,每个物品只放入背包中一次,即枚举的时候从最大的开始,先统计此时的方案数,然后将物品放入背包,这样一直下去,总复杂度O(n*m) 即背包装载容量应为[m-w[u]+1,m]   (1<=u<=n)

一般方法:

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n, m, ans;
int w[31], sum[31];
int dp[1003];
int main()
{
    int cas, i, j, u, ca=1;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%d",&w[i]);
        sort(w+1,w+n+1);
        printf("%d ",ca++);
        if(w[1]>m){printf("0\n");continue;}
        sum[0]=0;ans=0;
        for(i=1;i<=n;i++)
            sum[i]=sum[i-1]+w[i];
        for(u=1;u<=n;u++)//枚举每个不放入背包的体积最小的
        {
            memset(dp,0,sizeof(dp));
            dp[0]=1;
            for(i=u+1;i<=n;i++)
                for(j=m-sum[u-1];j>=w[i];j--)//前u个物品都放入了背包中
                    dp[j]+=dp[j-w[i]];
            for(j=max(0,m-sum[u-1]-w[u]+1);j<=m-sum[u-1];j++)
                ans+=dp[j];
        }
        printf("%d\n",ans);
    }
    return 0;
}

优化:

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int sum[35],w[35];
int dp[1005];
int n, m, ans;
int main()
{
    int cas, i, j, k, ca=1;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            scanf("%d",&w[i]);
        sort(w+1,w+1+n);
        printf("%d ",ca++);
        if(w[1]>m){printf("0\n");continue;}
        sum[0]=0;
        for(i=1;i<=n;i++)
            sum[i]=sum[i-1]+w[i];
        ans=0;
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        for(i=n;i>=1;i--)
        {
            for(j=max(0,m-sum[i-1]-w[i]+1);j<=m-sum[i-1];j++)
                ans+=dp[j];
            for(j=m;j>=w[i];j--)
                dp[j]+=dp[j-w[i]];
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2012-08-15 10:23  To be an ACMan  Views(347)  Comments(0)    收藏  举报