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

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
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章