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