L3-001. 凑零钱-PAT团体程序设计天梯赛GPLT(01背包,动态规划)

韩梅梅喜欢满宇宙到处逛街。现在她逛到了一家火星店里,发现这家店有个特别的规矩:你可以用任何星球的硬币付钱,但是绝不找零,当然也不能欠债。韩梅梅手边有 104 枚来自各个星球的硬币,需要请你帮她盘算一下,是否可能精确凑出要付的款额。

输入格式:

输入第一行给出两个正整数:N(≤\(10^4\))是硬币的总个数,M(≤\(10^2\))是韩梅梅要付的款额。第二行给出 N 枚硬币的正整数面值。数字间以空格分隔。

输出格式:

在一行中输出硬币的面值 V1≤V2≤⋯≤V**k,满足条件 V1+V2+...+V**k=M。数字间以 1 个空格分隔,行首尾不得有多余空格。若解不唯一,则输出最小序列。若无解,则输出 No Solution

注:我们说序列{ A[1],A[2],⋯ }比{ B[1],B[2],⋯ }“小”,是指存在 k≥1 使得 A[i]=B[i] 对所有 i<k 成立,并且 A[k]<B[k]。

输入样例 1:

8 9
5 9 8 7 2 3 4 1

输出样例 1:

1 3 5

输入样例 2:

4 8
7 2 4 3

输出样例 2:

No Solution

分析:01背包问题,因为要输出从小到大的排列,可以先把硬币面额从大到小排列,然后用bool类型的 \(choice[i][j]\) 数组 \(dp[i][j]\)是否选取,如果选取了就令choice为true;然后进行01背包问题求解,如果最后求解的结果不是恰好等于所需要的价值的,就输出No Soultion,否则从 \(choice[i][j]\) 判断选取的情况,i从n到1表示从后往前看第i个物品的选取情况,j从m到0表示从容量m到0是否选取(j = j – w[i]),把选取情况压入arr数组中,最后输出arr数组

// Murabito-B 21/04/23
#include <bits/stdc++.h>
using namespace std;
using ll    = long long;
const int N = 1e4 + 10;
int dp[N], w[N];
bool vis[N][N];
void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> w[i];
    sort(w + 1, w + 1 + n, greater<int>());
    for (int i = 1; i <= n; ++i)
        for (int j = m; j >= w[i]; --j)
            if (dp[j] <= dp[j - w[i]] + w[i])
                vis[i][j] = true, dp[j] = dp[j - w[i]] + w[i];
    if (dp[m] != m) cout << "No Solution";
    else {
        vector<int> a;
        int v = m, idx = n;
        while (v > 0) {
            if (vis[idx][v]) a.push_back(w[idx]), v -= w[idx];
            idx--;
        }
        for (int i = 0; i < a.size(); ++i) cout << a[i] << (i == a.size() - 1 ? "" : " ");
    }
}
int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    solve();
    return 0;
}
posted @ 2020-11-08 13:58  RioTian  阅读(178)  评论(1编辑  收藏  举报