POJ3260 The Fewest Coins 多重背包+完全背包

题意:说有一个人去买m元的东西,有n种钱币,没种钱币的面额是w[i],个数是C[i],售货员每种钱币有无数多个。现在这个人想让交易的钱个数最少,即找回的和付出钱的张数,最少。

多重背包+完全背包。

买家:多重背包;售货员:完全背包;

开两个数组,分别计算出买家,售货员每个面额的最少张数。

 

最重要的是上界的处理。可以注意到,上界为maxw*maxw+m(maxw最大面额的纸币),也就是24400元。(网上的证明)证明如下:

如果买家的付款数大于了maxw*maxw+m,即付硬币的数目大于了maxw,根据鸽笼原理,至少有两个的和对maxw取模的值相等,也就是说,这部分硬币能够用更少的maxw来代替。证毕。

真心不懂证明也没关系,记住结论就好了,真心不想记结论也没关系,数组开大一点就好了。

AC代码:

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 1<<29
int m, n;
int w[101],c[101];
int dp[24563],f[24563]; //dp[]买家,f[]售货员,注意上界证明
int main()
{
    int i, j, maxn;
    while(~scanf("%d%d",&n,&m))
    {
        maxn=0;
        for(i=1;i<=n;i++)
            scanf("%d",&w[i]),maxn=max(maxn,w[i]);
        for(i=1;i<=n;i++)
            scanf("%d",&c[i]);
        maxn=maxn*maxn+m+1;
        //多重背包
        fill(dp,dp+maxn+1,INF); //把dp[0]~dp[maxn]赋值INF
        dp[0]=0;
        for(i=1;i<=n;i++)
            if(c[i]*w[i]>=maxn)
            {
                for(j=w[i];j<=maxn;j++)
                    dp[j]=min(dp[j],dp[j-w[i]]+1);
            }
            else
            {
                int k=1;
                while(k<c[i])
                {
                    for(j=maxn;j>=w[i]*k;j--)
                        dp[j]=min(dp[j],dp[j-w[i]*k]+k);
                    c[i]-=k;
                    k*=2;
                }
                for(j=maxn;j>=w[i]*c[i];j--)
                    dp[j]=min(dp[j],dp[j-w[i]*c[i]]+c[i]);
            }
            //完全背包
        fill(f,f+maxn+1,INF);f[0]=0;
        for(i=1;i<=n;i++)
            for(j=w[i];j<=maxn;j++)
                f[j]=min(f[j],f[j-w[i]]+1);
        //统计最小值
        int ans=INF;
        for(i=0;i<=maxn-m;i++)
            ans=min(ans,dp[i+m]+f[i]);
        if(ans==INF)ans=-1; //不能找出,调整输出为-1.
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2012-08-14 09:32  To be an ACMan  Views(1083)  Comments(0)    收藏  举报