• 背包问题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;
    }
    
    

     

  • 相关阅读:
    linux 查看父进程号
    gitlab
    诺基亚C6常识详解
    C#.net书籍列表
    表链接
    Limu:JavaScript的那些书(转载)
    Oracle 多行记录合并/连接/聚合字符串的几种方法
    关于有锁iPhone的常识(转载)
    not in与not exists性能比较
    多表连接查询
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1954280.html
Copyright © 2020-2023  润新知