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)\)
posted @ 2020-09-03 09:31  童年の波鞋  阅读(150)  评论(0编辑  收藏  举报