b_pat_找更多硬币(求字典序最小的具体方案)
find more coin
有一天,她去了一家宇宙购物中心购物,结账时可以使用各种硬币付款。
但是,有一个特殊的付款要求:每张帐单,她都必须准确的支付所消费金额。
输入格式
第一行包含两个整数 N 和 M,分别表示硬币数量以及需要支付的金额。
第二行包含 N 个整数,表示每个硬币的面额。
输出格式
共一行,按照面额升序的顺序,输出用来支付的所有硬币的面额。
如果支付方式不唯一,则输出最小的支付面额序列。
如果无解,则输出 No Solution。
对于两个序列 {A[1], A[2], ...} 和 {B[1], B[2], ...},如果存在 k≥1 使得所有 i<k,满足 A[i]=B[i] 成立,并且 A[k]<B[k],则我们称序列 A 小于序列 B。
数据范围
1≤N≤10^4,
1≤M≤100,
硬币面值不超过 100
输入样例1:
8 9
5 9 8 7 2 3 4 1
输出样例1:
1 3 5
方法一:dp
题目描述的不是特别的准确,也没说硬币只能用一次还是无限使用。但有两点很明确:
- 支付方式可能不唯一
- 输出最小的支付面额序列(下面会讲到)
- 定义状态:
- f[i] ...
- g[i][j] 表示对于第 i 个硬币在背包容量为 j 时的选择状态(g 即下文的 chose 数组)
- 思考初始化:
- f[...]=0,g[..][...]=0;
- 思考状态转移方程:
- f[j]=f[j-A[i]]+A[i],g[i][j]=1;if (f[j]<=f[j-A[i]+A[i])(因为要字典序最小,当逆序排列后,如果每次有合法的状态都取更新 chose 数组,这样就一定可以取到字典序最小的方案)
- 思考输出 ...
为什么按升序排列取第一种方案不行呢?
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5, M=105;
int f[M], w[N], chose[N][M];
bool cmp(int a, int b) {
return a>b;
}
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n,m; cin>>n>>m;
for (int i=0; i<n; i++) cin>>w[i];
sort(w, w+n, cmp);
for (int i=0; i<n; i++)
for (int j=m; j>=w[i]; j--) if (f[j-w[i]]+w[i]>=f[j]) {
f[j]=f[j-w[i]]+w[i];
chose[i][j]=1;
}
if (f[m]==m) {
int j=m;
vector<int> ans;
for (int i=n-1; i>=0 && j; i--) if (chose[i][j]) {
ans.push_back(w[i]);
j-=w[i];
}
cout<<ans[0];
for (int i=1; i<ans.size(); i++) {
cout<<' '<<ans[i];
}
} else {
cout<<"No Solution";
}
return 0;
}
复杂度分析
- Time:\(O(nm)\),
- Space:\(O(m)\)