**1068. Find More Coins (30)背包,动态规划

PAT-A,1068,题目地址:https://www.patest.cn/contests/pat-a-practise/1068
这是一道01背包问题,解题时注意题意的转化:

  1. 可以将每个coin都看成value和weight都相同的物品
  2. 要求所付的钱刚刚好,相当于要求背包必须刚好塞满,且价值最大。(限制背包体积相当于限制coin的总和不能超过所要付的钱,在此条件下求coin组合的最大值,如果这个最大值刚好等于要付的钱,则有解,此时背包也刚好处于塞满状态,否则无解)
  3. 最后要求从小到大输出coin的组合,且有多解时输出最小的组合。这是此题的难点所在,我们应该将coin从大到小排序,在放进背包时也从大到小逐个检查物品,更新背包价值的条件是在加入一个新的物品后,价值>=原价值,注意此时等号的意义,由于物品是从大到小排序的,如果一个新的物品的加入可以保证价值和原来相同,则此时一定是发现了更小的组合。

代码如下:

#include 
#include 

using namespace std;

int bags[101];
bool selected[10001][101];

bool cmp(const int &a, const int &b){
    return a > b;
}

int main(){
    int coins[10001];
    int n, m;// n coins, pay m
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        cin >> coins[i];
    }
    sort(coins + 1, coins + 1 + n, cmp);

    for(int i = 1; i <= n; i++){
        for(int j = m; j >= 1; j--){ //之所以要反着来,和背包问题的更新规则有关
            if(j - coins[i] >= 0 && bags[j-coins[i]] + coins[i] >= bags[j]){ //等号必须取到,否则输出的解是最大的sequence
                selected[i][j] = true; //跟踪哪个物品被选择了
                bags[j] = bags[j-coins[i]] + coins[i];
            }
        }
    }
    if(bags[m] != m){
        cout << "No Solution" << endl;
        return 0;
    }
   //下面是输出最优组合的过程,其实和背包问题的更新规则有关,就是沿着选出解的路径,反着走回去,就找到了所有被选择的数字。
    int j = m, i = n;
    while(1){
        if(selected[i][j] == true){
            cout << coins[i];
            j -= coins[i];
            if(j != 0)
                cout << " ";
        }
        i--;
        if(j == 0 || i == 0)
            break;
    }
    cout << endl;
    return 0;
}

你可能感兴趣的:(**1068. Find More Coins (30)背包,动态规划)