背包问题--POJ 3260 The Fewest Coins【完全背包+多重背包】

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;
}

 

posted @ 2011-02-14 13:44  AndreMouche  阅读(1037)  评论(0编辑  收藏  举报