[PAT解题报告] Find More Coins
不难的题,给定n个硬币,问能否凑出m块钱。如果有解,输出字典序最小的解。
经典背包问题。现判断解的存在性,我们把钱币按面值由小到大排序,大于m的扔掉,排好后假设是a[0..n - 1]。
我们用bool数组can[x][y]表示用[x..n - 1]的硬币是否能凑出y块钱,
初值全是false,只有can[n][0] = true, 因为这表示什么硬币都不用,我们只能凑出0块钱。
递推:
can[i][j] = can[i + 1][j] || can[i + 1][j - a[i]];
这表示要用[i..n - 1]的硬币凑出j块钱,要么就是用[i + 1.. n - 1]已经凑好了,这样不需要第i个硬币。
要么用第i个硬币,并用[i + 1..n - 1]个硬币凑出j - a[i]块钱。
这样我们打出一个表来。
如何求字典序最小的解?
从第0个硬币开始考虑,对于第i个硬币,如果a[i + 1][m -
a[i]]为真,表示我们可以选择第i个硬币,并用剩余硬币凑出后面的钱,就选择第i个硬币,并且m -=
a[i]。这样我们就是从面值最小的考虑,在保证有解的前提下,尽可能选择面值较小的硬币,所以得到的解是字典顺序最小的。
代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
bool can[10002][102];
int a[10002];
int main() {
int n,m;
scanf("%d%d",&n,&m);
for (int i = 0; i < n; ++i) {
scanf("%d",&a[i]);
if (a[i] > m) {
--i;
--n;
}
}
sort(a, a + n);
can[n][0] = true;
for (int i = n - 1; i >= 0; --i) {
for (int j = 0;j <= m; ++j) {
can[i][j] = can[i + 1][j] || ((j >= a[i]) && can[i + 1][j - a[i]]);
}
}
if (can[0][m]) {
bool have = false;
for (int i = 0; (i < n) && (m >= a[i]); ++i) {
if (can[i + 1][m - a[i]]) {
if (have) {
putchar(' ');
}
else {
have = true;
}
printf("%d",a[i]);
m -= a[i];
}
}
puts("");
}
else {
puts("No Solution");
}
return 0;
}
原题链接: http://www.patest.cn/contests/pat-a-practise/1068

