您的位置:首页 > Web前端

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

2014-05-20 21:11 459 查看
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 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
————————————————————混乱的分割线————————————————————
思路:这道题就是传说中的混合背包。有一点不一样,那就是这道题不关心物品的价值,只关心物品的体积和个数。要找到最小的物品个数,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: