天天看點

POJ 3260 The Fewest Coins(混合背包+鴿巢原理)

題意:有某些硬币,已知某人分别有這些硬币數ci,賣家有這些硬币無限數量,已知待買的商品價格,求這個人所花的硬币數加上找零的硬币數的和最少是多少

思路:不難看出一個多重背包和完全背包即可

主要難點在于那個人應該出多少錢?出錢的上界是什麼,一開始我以為隻要出了超過最大面額的錢就可以了,結果wa後意識到錯了但也沒想到如何确定上界的方法。

看了别人的部落格學到:出錢的上界不會超過最大面額(mmax)的平方

反證:超過最大面額的平方是以找零的硬币數會超過最大面額值,把這些找零的所有硬币排成一列,sum[0...i]對mmax取模由鴿巢原理知:一定會出現兩個模相同的,那麼這兩個i,j之間的硬币就不需要産生了,是以一定不是最小硬币數的方式

//300 KB	94 ms	C++	1225 B
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,t;
int c[110],v[110];
int dp2[125*125];
int dp1[11000+125*125];
int main()
{
    while(~scanf("%d%d",&n,&t))
    {
        int mmax=-1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&v[i]);
            if(mmax<v[i]) mmax=v[i];
        }
        mmax*=mmax;
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        memset(dp2,0x3f,sizeof(dp2));
        dp2[0]=0;
        for(int i=1;i<=n;i++)
            for(int j=v[i];j<=mmax;j++)
                dp2[j]=min( dp2[j],dp2[j-v[i]]+1 );

        memset(dp1,0x3f,sizeof(dp1));
        dp1[0]=0;
        for(int i=1;i<=n;i++)
            for(int k=1;c[i];k<<=1)
            {
                int mul=min(k,c[i]);
                for(int j=t+mmax;j>=mul*v[i];j--)
                    dp1[j]=min(dp1[j],dp1[j-mul*v[i]]+mul);
                c[i]-=mul;
            }
        int ans=inf;
        for(int i=t;i<=t+mmax;i++)
        {
            if(dp1[i]>=inf||dp2[i-t]>=inf) continue;
            ans=min(ans,dp1[i]+dp2[i-t]);
        }
        if(ans==inf) puts("-1");
        else printf("%d\n",ans);
    }
    return 0;
}