零钱兑换---背包DP---完全背包

题目描述

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11

输出:3

解释:11 = 5 + 5 + 1

思路

代码

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Solution {////返回可以凑成总金额所需的 最少的硬币个数
public:
    int coinChange(vector<int>& coins, int amount) {
        int n = coins.size();

        // 二维数组:状态定义:dp[i][j]表示从前 i 个物品中选择不超过 j 重量的物品的最大价值
        vector<vector<int>> dp(n + 1, vector<int>(amount + 1, amount + 1));

        dp[0][0] = 0;

        // Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= amount; j++) {
                // 背包容量小于当前物品的容量,即背包容量已经不足以拿第 i 个物品了
                // 第 i 个物品即下标为 i - 1 的那个物品
                if (j < coins[i - 1]) {
                    dp[i][j] = dp[i - 1][j];
                }
                // 背包容量足够拿第 i 个物品,那么可拿也可不拿
                // 1、拿了,那么最大价值是前 i 个物品扣除第 i 个物品的重量的情况下的最大价值,再加上【第 i 个】物品的价值
                // 2、不拿,那么就是从前 i - 1 个物品中选择出的最大价值
                else {
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
                }
            }
        }

        return dp[n][amount] == amount + 1 ? -1 : dp[n][amount];//如果没更新,即找不到组合,就输出-1
    }
};

class Solution2 {////返回可以凑成总金额所需的 最少的硬币个数
public:
    int coinChange(vector<int>& coins, int amount) {
        int Max = amount + 1;
        vector<int> dp(amount + 1, Max);
        dp[0] = 0;
        for (int i = 1; i <= amount; ++i) {
            for (int j = 0; j < (int)coins.size(); ++j) {
                if (coins[j] <= i) {
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
};

vector<int> split(string params_str) {
    vector<int> p;
    while (params_str.find(",") != string::npos) {
        int found = params_str.find(",");
        p.push_back(stoi(params_str.substr(0, found)));
        params_str = params_str.substr(found + 1);
    }
    p.push_back(stoi(params_str));
    return p;
}

int main() {

    string input_str;
    cin >> input_str;//coins = [1,2,5]
    vector<int> coins;
    coins = split(input_str.substr(1, input_str.size() - 2));

    int amount;
    cin >> amount;//amount = 11
    Solution a;

    cout << a.coinChange(coins, amount) << endl;
}

变型题

题目要求:

返回可以凑成总金额的硬币组合数

代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {

        // 经典的完全背包问题

        // 二维数组:状态定义:dp[i][j]表示从数组 coins 中前 i 个硬币中选择得到价值为 j 的硬币组合数
        int n = coins.size();
        vector<vector<int>>dp(n + 1, vector<int>(amount + 1 , 0));

        // 只有当不选取任何硬币时,金额之和才为 0,此时只有 1 种硬币组合
        dp[0][0] = 1;

        // Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
        // coins 数组的大小就是硬币的个数
        for( int i = 1 ; i <= n ; i++){
            // 遍历背包容量
            for( int j = 0 ; j <= amount ; j++){

                // 背包容量小于当前硬币的值,即背包容量已经不足以拿第 i 个硬币了
                // 第 i 个硬币即下标为 i - 1 的那个硬币
                if( j < coins[i - 1]){

                    // 硬币组合数就是去考虑当前背包容量情况下,从前 i - 1 个物品中选择出的硬币组合数
                    dp[i][j] = dp[i - 1][j];

                // 背包容量足够拿第 i 个物品,那么可拿也可不拿
                // 1、拿了,那么硬币组合数是前 i 个硬币扣除第 i 个硬币的情况下的硬币组合数
                // 2、不拿,那么就是从前 i - 1 个硬币中选择出的硬币组合数
                }else{

                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
                }

            }

        }

        return dp[n][amount];

    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值