hdu 6685 (2019年杭电多校赛第九场第6题,最少硬币数目问题(枚举加贪心))

Rikka需要面对不同价格的餐品,当地有四种面值的硬币:10, 20, 50, 100。她希望找出最小数量的硬币,使得无论选择哪个价格的餐品都能支付且不找零。题目要求计算最少硬币数,如果无法满足所有价格则输出-1。通过将面值除以10并简化问题,可以使用枚举和贪心策略找到解决方案。" 114356476,10548922,PHP APCu扩展详解与使用,"['PHP扩展', '缓存技术', 'APCu']
摘要由CSDN通过智能技术生成

Rikka with Coin
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 851 Accepted Submission(s): 255

Problem Description
Rikka hates coins, and she used to never carry any coins with her. These days, Rikka is doing her summer internship abroad. Without mobile payment, Rikka has to face strange prices of commodities, and as a result of always using paper currency, she has to face mountainous coins on here table.

In the local currency system, there are 4 kinds of coins: 10 cents, 20 cents, 50 cents and 1 dollar. Up to now, Rikka has gained at least 10100 coins for each kind.

Now, Rikka is going to have dinner in the canteen, and she decides to pay the bill only with coins. There are n different combos in the canteen and the price of the ith is wi cents. Rikka would like to choose one combo as dinner but she has not decided to choose which one yet. Therefore, she wants to take some coins so that whichever she chooses, she can always pay the bill without receiving any change.

Since Rikka hates coins, she wants to carry as few coins as possible with her. As it is generally known that Rikka is not good at math, she wants you to help her make the decision.

Input
The first line of the input contains a single integer T(1≤T≤500), the number of test cases.

For each test case, the first line contains a single integer n(1≤n≤100), the number of combos sold in the canteen.

The second line contains n positive integers w1,…,wn(1≤wi≤109), which represents the prices.

Output
For each test case, output a single line with a single integer which represents the minimum number of coins. If there is no valid solution, output −1.

Hint
In the first test case, one optimal solution is to bring one coin of 10 cents and two coins of 20 cents.

In the second test case, one optimal solution is to bring 5 coins of one dollar.

Sample Input
3
5
10 20 30 40 50
5
100 200 300 400 500
1
1

Sample Output
3
5
-1
题意: 给出无限个面值为10, 20, 50, 100的硬币,给出n个面额值,找出最少的硬币数目满足所有的金额值,若不存在,则输出-1.
思路: 先对面值和金额进行除10处理,换成由1 2 5 10组成对应的金额。因为10可由1 2 5 组成,所以对于每次枚举,对1 2 5的硬币值进行枚举,同时再找出10的至少数目,最后获取所有的解去最小值就是答案,(eg:10个数,分别为10 20 30 40 50 60 70 80 90 100,我只需要一个10,两个20和一个50就可以满足所有的条件),详情看代码和注释,最后附带一些样例。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const int NN = 1e2 + 5;
const int inf = 1e9 + 7;
bool dp[N];
int a[NN], v[N];
int m;
int f(int x, int y, int z) {
	int tot = 0, one = 0;
	// 找出由1 2 5组成的美分数目 
	int maxn = 1 * x + 2 * y + 5 * z;
	for (int i = 0; i <= maxn; i++) dp[i] = false;
	for (int i = 1; i <= x; i++) v[++tot] = 1;
	for (int i = 1; i <= y; i++) v[++tot] = 2;
	for (int i = 1; i <= z; i++) v[++tot] = 5;
	dp[0] = true;
	// 标记可由1 2 5组成的面额值
	for (int i = 1; i <= tot; i++) {
		for (int j = maxn; j >= v[i]; j--) {
			dp[j] = dp[j] | dp[j - v[i]];
		}
	}
	int tmp, cur, mid;
	for (int i = 1; i <= m; i++) {
		tmp = a[i] / 10; // 100美分的数目 
		mid = a[i] % 10; // 除100美分的剩余金额 
		cur = inf;
		if (mid > maxn) return -1;
		if (dp[mid]) cur = min(cur, tmp); // 算出当前100美分的数值 
		// 找出最少的100美分所需数量,就是减去由10,20,50能替换100的数目,注意在原来的数组上进行了除10的处理,所以是加10 
		while (mid + 10 <= maxn && tmp > 0) {
			tmp--;
			mid += 10;
			if (dp[mid]) cur = min(cur, tmp);
		}
		if (cur == inf) return -1;
		one = max(one, cur); // 找出至少需要100美分的数目 
	}
	return one + x + y + z; // 返回当前的满足条件的解 
}

int main() {
	int t, n, flag;
	scanf("%d", &t);
	while (t--) {
		flag = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			// 出现个位数不为0,直接标记无法组成 
			if (a[i] % 10 != 0) flag = 1;
		}
		if (flag == 1) {
			printf("-1\n");
		} else {
			// 全部除于10,转化为由1 2 5 10组成的硬币值 
			for (int i = 1; i <= n; i++) a[i] /= 10;
			sort(a + 1, a + n + 1);
			// 去重 
			m = unique(a + 1, a + n + 1) - a - 1;
			int ans = 1e9 + 7;
			// 枚举1 2 5 10所需要的数目是否满足所有条件,并从中找出最少硬币数目 
			// 2 3 2 的范围的确定,就是实际10的数目不会超过2,超过2会被20代替;同理20不超过3,50不超过2 
			for (int i = 0; i <= 2; i++) {
				for (int j = 0; j <= 3; j++) {
					for (int k = 0; k <= 2; k++) {
						int num = f(i, j, k);
						if (num != -1) { // 此时有解的时候更新最小值 
							ans = min(ans, num);
						}
					}
				}
			}
			printf("%d\n", ans);
		}
	}
	return 0;
} 
/*
1
10
10 20 30 40 50 60 70 80 90 100
4

1
7
10 20 30 40 50 200 300
6

1
2
60 40
3

1
7
20 40 80 240 220 180 200
6

1
3
340 260 120
6

1
4
140 110 50 120

1
5
200 410 240 270 290
7

1
7
110 210 190 110 310 200 310
6
*/
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值