【DP|混合背包】POJ-3260 The Fewest Coins

69 篇文章 0 订阅
13 篇文章 0 订阅
这篇博客探讨了ACM竞赛中的混合背包问题,重点在于如何找到最小的物品个数来满足特定体积。文章指出,不同于传统的背包问题,该题不关注物品价值,只考虑体积和数量。找钱过程采用完全背包,付钱过程转化为01背包问题。付钱的体积上限与找钱的上限有关,找钱上限通常设定为最大面额的平方。作者提供了代码实现,并提及解决方案可能依赖于一些猜测和人品因素。
摘要由CSDN通过智能技术生成
The Fewest Coins
Time Limit: 2000MS Memory Limit: 65536K
   

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 V1V2, ..., VN (1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1C2 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  V 1V 2, ...,  VN coins ( V 1, ... VN
Line 3: N space-separated integers, respectively  C 1C 2, ...,  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

————————————————————混乱的分割线————————————————————

思路:这道题就是传说中的混合背包。有一点不一样,那就是这道题不关心物品的价值,只关心物品的体积和个数。要找到最小的物品个数,f[ i ]数组的值不再表示最大价值、改成表示使用个数。

即“体积为 i 的时候物品的个数”。

找钱的过程显然是完全背包,而付钱的过程是多重背包,【拆分成01背包即可】,每次减去k倍的体积,增加个数k即可。

现在问题集中在付钱的过程,因为拆分成了01背包,所以循环应该是逆序写的。那么上限是什么?在付钱的时候,显然是需要付出大于等于价格T,也就是说付钱的背包体积是更大的!大多少呢?

如何保证最优解在01背包的体积上限范围内?

据说是根据找钱的上限推出的付钱上限。姑且不谈如何找付钱上限。如果存在付钱上限M,那么找钱上限就是T+M。也就是说,假如找钱不超过M,一定不会丢失最优解。

找钱的上限是——最大面额的平方。

QAQ TAT 点儿波浪线点儿

反正……蒙一个也许能蒙对。10000+price是能过的……看人品吧!

如果你需要证明:

///反证法。假设存在一种支付方案,John给的钱超过T+maxValue^2, 
///则售货员找零超过maxValue^2,则找的硬币数目超过maxValue个,将其看作一数列,
///求前n项和sum(n),根据鸽巢原理,至少有两个对maxValue求模的值相等,
///假设为sum(i)和sum(j),i<j,则i+1...j的硬币面值和为maxValue的倍数,
///同理,John给的钱中也有 一定数量的硬币面值和为maxValue的倍数,
///则这两堆硬币可用数量更少的maxValue面值硬币代替,产生更优方案。


代码如下:(代码给出了YY版和正式版,正式版在注释中)

/****************************************/
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <algorithm>
 #include <cmath>
 #include <stack>
 #include <queue>
 #include <vector>
 #include <map>
 #include <string>
 #include <iostream>
 using namespace std;
/****************************************/
const int SUP = 10000 + 10010;//14400+10010;
const int INF = 2e8;//小心加法溢出
int f1[SUP], f2[SUP];
int coin[105], Bag[105];

void Zero_pack(int cost, int V, int k)
{
	for(int j = V; j >= cost; j--)
		f1[j] = min(f1[j], f1[j-cost] + k);
}

void Comp_pack(int cost, int V)
{
	for(int j = cost; j <= V; j++)
		f2[j] = min(f2[j], f2[j-cost] + 1);
}

void Multi_pack(int cost, int V, int bag)
{
	//二进制法对coins的个数进行转化
	for(int k = 1; k < bag; k <<= 1) {
		Zero_pack(k*cost, V, k);//01背包解法
		bag -= k;
	}
	Zero_pack(bag*cost, V, bag);
}

int main()
{
	int n, price;
	scanf("%d%d", &n, &price);
	int max_coin = 0;
	for(int i = 0; i < n; i++) {
		scanf("%d", &coin[i]);
		max_coin = max(max_coin, coin[i]);//记录最大面额
	}
	
	//int max_price = max_coin * max_coin + price;//背包容量上限即最大面额的平方加上目标容量
	for(int i = 0; i < n; i++) 
		scanf("%d", &Bag[i]);
	
	for(int i = 1; i <= 10000+price; i++)//max_price; i++)
		f1[i] = f2[i] = INF;
	f1[0] = f2[0] = 0;
	
	//接下来各自对付钱和找钱的过程进行DP,f[v]表示达到价格v需要多少个coins
	///找钱的上限是付钱的上限减去price
	for(int i = 0; i < n; i++) {
		Comp_pack(coin[i], 10000);//max_coin*max_coin);
		Multi_pack(coin[i], 10000+price, Bag[i]);//max_price, Bag[i]);
	}
	
	//接下来寻找答案,枚举一遍找到最优解
	int ans = INF;
	for(int i = price; i <= 10000+price; i++)//max_price; i++)
		ans = min(ans, f1[i] + f2[i-price]);
	if(ans == INF)
		puts("-1");
	else
		printf("%d\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值