杭电2844--Coins

题目描述:
这里写图片描述
解析:
知道这是多重背包问题,但是初做这道题时我还是不知道用什么作为背包容量,而通过分析这道题发现,题目所求是价值不超过m的方案的个数。对于(1–m)内的任意一个价值,如果最佳方案dp[i]达到了这个值,那么就可以说存在这种方案。因此建立如下转移方程:
dp[i][j]=max{dp[i-1][j],dp[i-1][j-v[i]]+v[i]}
其中dp[i][j]表示的是装i件物品,价值不超过j的最佳方案。因为要求价值不超过m,因此我们以钱币的价值作为所占体积,同时也作为物品的价值,那么就得到了以上方程。
优化:
参考《背包九讲》:
(1)对于一种物品i,如果存在令一种物品j,使得W[j]<=W[i],val[j]>=val[i]那么显然这件物品i就可以被舍弃,通过O(n^2)的时间复杂度就可以完成数据的筛选。但这样做很有可能无济于事,因为很有可能并没有两队数据满足这样的条件,却依旧执行了O(n^2)的遍历。
(2)对于完全背包问题(多重背包类似),其实可以转化成0-1背包问题来做。假设背包的总容量为V,第i件物品所占的体积为W[i],那么无论怎么装,第i件物品最多就只能装V/W[i]件,对于每件物品如上处理并依次存入数组,就将完全背包问题转化成了0-1背包问题,可是这样的时间复杂度依旧是O(NV*V/W[i]),并没有优化时间复杂度。
(3)一个很好的优化方法:
考虑二进制的思想,2^n罗列如下:1 2 4 8 16 32 64……
把第i种物品拆成费用为c[i]*2^k、价值为w[i]*2^k的若干件物品,其中k满足c[i]*2^k<=V。这是二进制的思想,因为不管最优策略选几件第i种物品,总可以表示成若干个2^k件物品的和。例如:5=2^2+2^0 7=5+2^1等。这样把每种物品拆成O(log(V/c[i]))件物品,是一个很大的改进。
还有一点值得注意的是,我提交了8遍全都惨遭TLE,发现是初始化dp[]数组的时候出了问题,我将dp做了最大程度上的初始化,这就超了时…改进就是,用多少初始化多少就好了。

代码:

#include<stdio.h>
#include<string.h>
#define MAXN 100010
#define max(a,b) (a>b?a:b)
int main()
{
    int m,n,val[110],v[10010],cnt,amt,dp[MAXN];
    int i,j,k,t;
    while(scanf("%d%d",&n,&m)!=EOF&&(m+n))
    {
        t=1;
        amt=0;
        for(i=0;i<=m;i++) dp[i]=0;
        for(i=1;i<=n;i++)
            scanf("%d",&val[i]);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&cnt);
            for(j=1;j<=cnt;j<<=1)
            {
                v[t++]=j*val[i];
                cnt-=j;
            }
            if(cnt>0)
            v[t++]=cnt*val[i];
        }
        for(i=1;i<t;i++)
        {
            for(j=m;j>=v[i];j--)    //dp[j]表示价格不超过m的方案的最优价值 
            dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
        }
        for(i=1;i<=m;i++)
            if(dp[i]==i) amt++;
        printf("%d\n",amt);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cprimesplus/article/details/82496066