【poj3260-最少找零】多重背包+完全背包

多重背包+完全背包。

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

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

 

最重要的是上界的处理:上界为maxw*maxw+m(maxw最大面额的纸币)。

(网上的证明)证明如下:

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

 

其实我真心没看懂这个证明。不过我们可以猜想T一定不会太大,我开了10*T然后过了。

这一题学习了多重背包:

http://www.cnblogs.com/favourmeng/archive/2012/09/07/2675580.html

 

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<iostream>
 6 #include<algorithm>
 7 using namespace std;
 8 
 9 const int N=110,S=100100,INF=(int)1e9;
10 int n,T,mx,v[N],c[N],f[S],g[S];
11 int minn(int x,int y){return x<y ? x:y;}
12 int maxx(int x,int y){return x>y ? x:y;}
13 
14 int main()
15 {
16     // freopen("a.in","r",stdin);
17     freopen("fewcoins.in","r",stdin);
18     freopen("fewcoins.out","w",stdout);
19     scanf("%d%d",&n,&T);
20     mx=0;
21     for(int i=1;i<=n;i++) {scanf("%d",&v[i]);mx=maxx(mx,v[i]);}
22     for(int i=1;i<=n;i++) scanf("%d",&c[i]);
23     mx=mx*mx+T;
24     // mx=10*T;
25     memset(f,63,sizeof(f));
26     f[0]=0;
27     for(int i=1;i<=n;i++)
28         for(int j=v[i];j<=mx;j++)
29             f[j]=minn(f[j],f[j-v[i]]+1);
30                 
31     memset(g,63,sizeof(g));
32     g[0]=0;
33     for(int i=1;i<=n;i++)
34     {
35         int now=c[i],j=1;
36         while(now>0)
37         {
38             for(int k=mx;k>=j*v[i];k--)
39                 g[k]=minn(g[k],g[k-j*v[i]]+j);    
40             now-=j;
41             j*=2;
42         }        
43     }
44     int ans=INF;
45     for(int i=T;i<=mx;i++) 
46         ans=minn(ans,g[i]+f[i-T]);
47     if(ans==INF) printf("-1
");
48     else printf("%d
",ans);
49     return 0;
50 }

 

原文地址:https://www.cnblogs.com/KonjakJuruo/p/5943696.html