完全背包

来源:互联网 发布:三国演义版本 知乎 编辑:程序博客网 时间:2024/06/11 19:05
//poj 3260 The Fewest Coins/*题意:John带了n种币值Vi的确定数量Ci的硬币,而shopkeeper的硬币无限多.给出T,求John支付的硬币数目加上售货员找零的硬币数目的最小值。如果无法支付T,输出-1 支付时硬币数量有限制,为多重背包问题. 找零时硬币数量无限制,为完全背包问题*/#include<iostream>        //多重背包和完全背包using namespace std;    int main()    {    int n,t,euro[110],num[110],dp[30000],maxn;    cin>>n>>t;    int mx=0;    for(int i=1;i<=n;++i)    {        cin>>euro[i];        mx=max(mx,euro[i]);    }    maxn=mx*mx+t;        //上界    for(int i=1;i<=n;++i)        cin>>num[i];    fill(dp,dp+maxn+1,-1);    dp[0]=0;    //John付钱  多重背包,通过二进制方法转化为01背包    for(int i=1;i<=n;++i)    {        int k=1,s=num[i];        while(s>=k)        {            for(int j=maxn;j>=euro[i]*k;--j)            if(dp[j-euro[i]*k]!=-1)            {                if(dp[j]==-1)                    dp[j]=dp[j-euro[i]*k]+k;    //注意是 +k                else                    dp[j]=min(dp[j],dp[j-euro[i]*k]+k);            }            s-=k;k*=2;            }        for(int j=maxn;j>=euro[i]*s;--j)            if(dp[j-euro[i]*s]!=-1)            {                if(dp[j]==-1)                    dp[j]=dp[j-euro[i]*s]+s;                else                    dp[j]=min(dp[j],dp[j-euro[i]*s]+s);            }    }    //shopkeeper找钱  完全背包    for(int i=1;i<=n;++i)        {        for(int j=maxn-euro[i];j>0;--j)            //因为是减,所以要逆序循环            if(dp[j+euro[i]]!=-1)            {                if(dp[j]==-1)                    dp[j]=dp[j+euro[i]]+1;                else                    dp[j]=min(dp[j],dp[j+euro[i]]+1);            }    }        cout<<dp[t]<<endl;    return 0;}/*上界为:T+maxValue^2,其中maxValue为最大硬币面值。证明:反证法。假设存在一种支付方案,John给的钱超过T+maxValue^2, 则售货员找零超过maxValue^2,找的硬币数目超过maxValue个,将其看作一数列,求前n项和sum(n),根据鸽巢原理,至少有两 个对maxValue求模的值相等,假设为sum(i)和sum(j),i<j,则i+1...j的硬币面值和为maxValue的倍数,同理,John给的钱中也有 一定数量的硬币面值和为maxValue的倍数,则这两堆硬币可用数量更少的maxValue面值硬币代替,产生更优方案。*/
原创粉丝点击