POJ 3260 The Fewest Coins 完全背包+多重背包

The Fewest Coins

Time Limit: 2000MS

 

Memory Limit: 65536K

Total Submissions: 7414

 

Accepted: 2293

Description

Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he always pays for his goods in such a way that the smallest number of coins changes hands, i.e., the number of coins he uses to pay plus the number of coins he receives in change is minimized. Help him to determine what this minimum number is.

FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, ..., VN (1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coins of value V2, ...., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner (although Farmer John must be sure to pay in a way that makes it possible to make the correct change).

Input

Line 1: Two space-separated integers: N and T
Line 2: N space-separated integers, respectively V1, V2, ..., VN coins (V1, ...VN
Line 3: N space-separated integers, respectively C1, C2, ..., CN

Output

Line 1: A line containing a single integer, the minimum number of coins involved in a payment and change-making. If it is impossible for Farmer John to pay and receive exact change, output -1.

Sample Input

3 70
5 25 50
5 2 1

Sample Output

3

Hint

Farmer John pays 75 cents using a 50 cents and a 25 cents coin, and receives a 5 cents coin in change, for a total of 3 coins used in the transaction.

Source

USACO 2006 December Gold

算法分析:

题意:

最小货币流通:用面值V[0...i],个数C[0…i]的硬币购买价格T的商品,假设商店每种面值的硬币都有无限个,求最小货币流通量。

分析:

流通分为付钱和找钱两个过程,定义两个过程分别需要的硬币数为如下dp数组:

int dp1[M] //dp1[i]找钱为i时交付的最小货币量

int dp2[M];  //dp2[i]付钱为i时交付的最小货币量

答案:

for(int i=0;i<=max_v*max_v;i++)

{

      ans=min(ans,dp1[i]+dp2[i+m]);

}

上面付出了T+i金额的钱,收回了i金额的钱,于是实际上就买到了价格T的商品

付钱:

每个硬币的携带量有限。将硬币视作物品,硬币的价格作为物品的重量,硬币的个数1作为物品的价值,则给定某个T作为背包容量W,问题转化为物品价值总和最小的多重背包问题。

找钱:

 

找钱阶段,硬币数量不限,思路就是完全背包。

确定背包容量

题目的难点其实在于背包容量W的确定,W的意义为最优方案金额的最大值

其中:给钱上界为: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面值硬币代替,产生更优方案。

代码实现:

#include<cstdio>  
#include<cstring>  
#include<cstdlib>  
#include<cctype>  
#include<cmath>  
#include<iostream>  
#include<sstream>  
#include<iterator>  
#include<algorithm>  
#include<string>  
#include<vector>  
#include<set>  
#include<map>  
#include<stack>  
#include<deque>  
#include<queue>  
#include<list>  
using namespace std;  
const double eps = 1e-8;  
typedef long long LL;  
typedef unsigned long long ULL;  
const int INT_INF = 0x3f3f3f3f;  
const int INT_M_INF = 0x7f7f7f7f;  
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;  
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;  
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};  
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};  
const int MOD = 1e9 + 7;  
const double pi = acos(-1.0);  
const int MAXN = 1e5 + 10;  
const int MAXT = 10000 + 10;  
const int M=10055+121*121;
int v[150],c[150];
int dp1[M],dp2[M];
//dp1[i]找钱为i时交付的最小货币量
//dp2[i]付钱为i时交付的最小货币量
void dp_complete_pack(int n, int W)    //n物品种类数,W为背包容量
{
    memset(dp1, 0x3f, sizeof(dp1));    
    dp1[0] = 0;
    for (int i = 0; i < n; ++i)
    {
        for (int j=v[i];j<=W;++j)     
        {
	
            dp1[j] = min(dp1[j],dp1[j-v[i]]+1); 
        }
    }
}
void dp_multiple_pack(int n,int W) //n物品种类数,W为背包容量
{
    memset(dp2, 0x3f, sizeof(dp2));
    dp2[0] = 0;
    for (int i=0;i<n;++i)
    {
        int num=c[i];
        for (int k=1; num>0; k<<=1)
        {
            int mul = min(k, num);
            for (int j= W; j>= v[i] * mul; --j)
            {
                dp2[j] = min(dp2[j], dp2[j - v[i] * mul] + mul);   // 价值为1
            }
            num-=mul;
        }
    }
}

int main()
{
  int n,m;
  while(scanf("%d%d",&n,&m)!=EOF)
  {
  	int max_v=0;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&v[i]);
		max_v=max(max_v,v[i]);
	}
	for(int i=0;i<n;i++)
		scanf("%d",&c[i]);
		
	dp_complete_pack(n,m+max_v*max_v);
	dp_multiple_pack(n,m+max_v*max_v);
	int ans=INT_INF;
	
	for(int i=0;i<=max_v*max_v;i++)
		{
			ans=min(ans,dp1[i]+dp2[i+m]);
			
		}
		if(ans==INT_INF)
			printf("-1\n");
		else
	       printf("%d\n",ans);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/sdz20172133/article/details/81264193