背包问题--POJ 3260 The Fewest Coins【完全背包+多重背包】
网上有篇很好的报告,觉得不错,就拷了些过来,(*^__^*) 嘻嘻……
网址:http://www.cppblog.com/Davidlrzh/articles/135614.html
题意:John去买东西,东西的价格是T(1 <= T <= 10000),John所在的地方有n(1 <= n <= 100)种的硬币,面值分别为V1, V2, ..., Vn (1 <= Vi <= 120)。John带了C1枚面值为V1的硬币,C2枚面值为V2的硬币,...,Cn枚面值为Vn的硬币(0 <= Ci <= 10000)。售货员那里每种硬币都有无限多个。问为了支付这个T,John给售货员的硬币数目加上售货员找回的零钱的硬币数目最少是多少。如果无法支付 T,输出-1 。
解法:支付时硬币数量有限制,为多重背包问题,通过二进制方法转化为01背包求解。找零时,硬币数量无限制,为完全背包问题。对两问题分别求解,然后找出差额为T时,两者和的最小值即为所示。
其中:给钱上界为: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面值硬币代替,产生更优方案。
分析:1.给钱时,硬币数有限制,为多重背包问题。
2.找钱时,硬币数无限制,为完全背包问题。
3.给钱上界为:T+maxValue^2,其中maxValue为最大硬币面值。证明:反证法。假设存在一种支付方案,John给的钱超过T+maxValue^2,
则售货员找零超过maxValue^2,则找的硬币数目超过maxValue个,将其看作一数列,求前n项和sum(n),根据鸽巢原理,至少有两
个对maxValue求模的值相等,假设为sum(i)和sum(j),i<j,则i+1j的硬币面值和为maxValue的倍数,同理,John给的钱中也有
一定数量的硬币面值和为maxValue的倍数,则这两堆硬币可用数量更少的maxValue面值硬币代替,产生更优方案。
#include<stdio.h> #include<string.h> const int T = 14400+1; const int T2 = T+14400+2; const int N = 101; int change[T];//change[i]表示商店找钱i所需的硬币数 int dp[T2];//dp[i]表示用户付钱i所需硬币数 int c[N]; int v[N]; __int64 upper; inline int min(int a,int b) { if(a==-1)return b; return a<b?a:b; } void init(int n) { memset(change,-1,sizeof(change)); int i,j; int upper = 0; change[0]=0; for(i=0;i<n;i++) { for(j=v[i];j<T;j++) { if(change[j-v[i]]!=-1) change[j]=min(change[j],change[j-v[i]]+1); } } } void zeroOnePack(int v,int c) { __int64 i; for(i=upper;i>=v;i--) { if(dp[i-v]!=-1) dp[i]=min(dp[i],dp[i-v]+c); } } int main() { int n,t; while(scanf("%d%d",&n,&t)!=EOF) { int i; for(i=0;i<n;i++) scanf("%d",&v[i]); for(i=0;i<n;i++) scanf("%d",&c[i]); if(t==0) { printf("0\n"); continue; } init(n); memset(dp,-1,sizeof(dp)); dp[0]=0; upper = 0; for(i=0;i<n;i++) { upper=upper+c[i]*v[i]; if(upper>=T2)upper = T2-1; int count = 1; while(count<c[i]) { zeroOnePack(v[i]*count,count); c[i]-=count; count<<=1; } zeroOnePack(v[i]*c[i],c[i]); } int ans = -1; for(i=t;i<=upper;i++) if(dp[i]!=-1&&(i-t)<T&&change[i-t]!=-1) ans=min(ans,dp[i]+change[i-t]); printf("%d\n",ans); } return 0; }