pat甲级1068 Find More Coins (30 分)

欢迎访问我的pat甲级题解目录

题目描述

pat甲级1068 Find More Coins (30 分)题目描述

算法设计

这是一道0-1背包问题。设 V i V_i 表示第 i i 个硬币的面值, d ( i , j ) d(i,j) 表示在第 1 , 2 , , i 1,2,\dots,i 个硬币中,任选一些硬币,是否能使总面值恰好为 j j 。假设所有硬币下标从1开始,那么边界条件为 d ( 0 , 0 ) = t r u e d(0,0)=true ,表示不选任何硬币的总面值为0。状态转移方程为

  1. j < V i d ( i , j ) = d ( i 1 , j ) j<V_i,d(i,j)=d(i-1,j)
  2. j > = V i d ( i , j ) = d ( i 1 , j ) d ( i 1 , j V i ) j>=V_i,d(i,j)=d(i-1,j)||d(i-1,j-V_i) ||是或关系运算符)

题目要比普通的0-1背包问题稍微麻烦一些,因为它要求打印字典序最小的解。为此,我们需要额外定义一个二维数组 s e l e c t select s e l e c t ( i , j ) select(i,j) 表示在组成总面值为 j j 的硬币中,第 i i 个硬币是否被选中。我们进行递推时,下标从1~n进行递推。这时,将硬币按照面值大小从大到小排序,如果有解,我们按下标从n~1进行打印即可。

C++代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<int> v(n + 1);
    vector<bitset<105>> dp(n + 1), select(n + 1);
    for (int i = 1; i <= n; ++i)
        cin >> v[i];
    sort(v.begin() + 1, v.end(), greater<int>());
    dp[0][0] = select[0][0] = true;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j <= m; ++j) {
            dp[i][j] = dp[i - 1][j];
            if (j >= v[i]) {
                dp[i][j] = dp[i][j] || dp[i - 1][j - v[i]];
                if (dp[i - 1][j - v[i]])
                    select[i][j] = true;
            }
        }
    }
    if (!dp[n][m]) {
        cout << "No Solution";
    } else {
        bool output = false;
        for (int i = n; i >= 1; --i)
            if (select[i][m]) {
                cout << (output ? " " : "") << v[i];
                m -= v[i];
                output = true;
            }
    }
    return 0;
}
发布了528 篇原创文章 · 获赞 1015 · 访问量 37万+

猜你喜欢

转载自blog.csdn.net/richenyunqi/article/details/103038877