第九章 动态规划part07(代码随想录)

完全背包的理论基础:装满背包它的最大价值是多少

零钱兑换II:装满背包有多少种方法

 70. 爬楼梯 (进阶) 

class Solution {
public:
    int climbStairs(int n) {
        // if (n<=1) return n;
        // int dp[3];
        // dp[1]=1;
        // dp[2]=2;
        // for (int i=3; i<=n; i++){
        //     int sum = dp[1]+dp[2];
        //     dp[1] = dp[2];
        //     dp[2] = sum;
        // }
        // return dp[2];
        int m = 2; // 代码中m表示最多可以爬m个台阶,代码中把m改成2就是本题70.爬楼梯可以AC的代码了。
        vector<int> dp(n+1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) { // 遍历背包
         for (int j = 1; j <= m; j++) { //遍历物品
                // 當前的背包容量 大於 物品重量的時候,我們才需要記錄當前的這個裝得方法(方法數+)
                if (i - j >= 0) dp[i] += dp[i - j];
            }
        }
        return dp[n];
    }
};

 322. 零钱兑换  

amount(总金额):背包容量

coins:物品

如果求组合数就是外层for循环遍历物品,内层for遍历背包。

如果求排列数就是外层for遍历背包,内层for循环遍历物品。

1. 确定dp[j] dp数组以及下标的含义

dp[j]:凑足总额为j所需钱币的最少个数为dp[j]

(装满容量为j,最少物品为dp[j])

2. 确定递推公式

凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])

dp[j - coins[i]] + 1 

递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]); 每加一个物品都是coins[i]+1变成dp[j]了,我应该取哪些dp[j]呢?应该取一个最小的

3. dp数组如何初始化

dp[0] = 0 // 和为0,凑满和0件物品

max初始成0,min初始成INT_MAX,这样递推值才不会被初始值覆盖

4. 确定遍历顺序

组合:先物品后背包 排列:先背包后物品

本题求组合或者排列都可以,不影响装满背包。

从j-coins[i]开始遍历,避免下标出现负数

5. 打印dp数组

322.零钱兑换

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, INT_MAX);
        // int dp[amount + 1];
        // memset(dp, INT_MAX, sizeof(dp));

        // for(int i=0; i < dp.size(); i++){
        //     cout << dp[i] <<'\n';
        // }
        dp[0] = 0; // 凑足总金额为0所需钱币的个数一定是0

        for (int i = 0; i < coins.size(); i++) { // 遍历物品
            for (int j = coins[i]; j <= amount; j++) { // 遍历背包
                // cout<< "here: " << dp[j - coins[i]] << '\n'; 
                if (dp[j - coins[i]] != INT_MAX) { // 如果dp[j - coins[i]]是初始值则跳过
                    dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
                }
            }
        }
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

memset() 设置的是字节的值,不是 int 类型的值。所以如果使用 memset() 来初始化 int 类型的数组,应该使用 sizeof(dp) 来计算需要设置的字节数,而不是数组的元素个数。 正确的写法是: memset(dp, 0x3f, sizeof(dp)); // 0x3f 表示 INT_MAX 的十六进制表示

// 版本二
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {  // 遍历背包
            for (int j = 0; j < coins.size(); j++) { // 遍历物品
                if (i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX ) {
                    dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
                }
            }
        }
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

i: 1 j: 0 i: 1 j: 1 i: 1 j: 2 i: 2 j: 0 i: 2 j: 1 i: 2 j: 2 i: 3 j: 0 i: 3 j: 1 i: 3 j: 2 i: 4 j: 0 i: 4 j: 1 i: 4 j: 2 i: 5 j: 0 i: 5 j: 1 i: 5 j: 2 i: 6 j: 0 i: 6 j: 1 i: 6 j: 2 i: 7 j: 0 i: 7 j: 1 i: 7 j: 2 i: 8 j: 0 i: 8 j: 1 i: 8 j: 2 i: 9 j: 0 i: 9 j: 1 i: 9 j: 2 i: 10 j: 0 i: 10 j: 1 i: 10 j: 2 i: 11 j: 0 i: 11 j: 1 i: 11 j: 2

i: 0 j: 1 i: 0 j: 2 i: 0 j: 3 i: 0 j: 4 i: 0 j: 5 i: 0 j: 6 i: 0 j: 7 i: 0 j: 8 i: 0 j: 9 i: 0 j: 10 i: 0 j: 11 i: 1 j: 2 i: 1 j: 3 i: 1 j: 4 i: 1 j: 5 i: 1 j: 6 i: 1 j: 7 i: 1 j: 8 i: 1 j: 9 i: 1 j: 10 i: 1 j: 11 i: 2 j: 5 i: 2 j: 6 i: 2 j: 7 i: 2 j: 8 i: 2 j: 9 i: 2 j: 10 i: 2 j: 11

 279.完全平方数  

不会拼凑不成,完全平方数有个1

1. 确定dp[j] dp数组以及下标的含义

dp[j]:和为j的完全平方数的最少数量为dp[j]

2. 确定递推公式

dp[j] 可以由dp[j - i * i]推出, dp[j - i * i] + 1 便可以凑成dp[j]。

此时我们要选择最小的dp[j],所以递推公式:dp[j] = min(dp[j - i * i] + 1, dp[j]);

3. dp数组如何初始化

dp[0]=0完全是为了递推公式。

从递归公式dp[j] = min(dp[j - i * i] + 1, dp[j]);中可以看出每次dp[j]都要选最小的,所以非0下标的dp[j]一定要初始为最大值,这样dp[j]在递推的时候才不会被初始值覆盖

4. 确定遍历顺序

如果求组合数就是外层for循环遍历物品,内层for遍历背包。

如果求排列数就是外层for遍历背包,内层for循环遍历物品。

所以本题外层for遍历背包,内层for遍历物品,还是外层for遍历物品,内层for遍历背包,都是可以的!

5. 打印dp数组

279.完全平方数

最后的dp[n]为最终结果。 

// 版本二
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i * i <= n; i++) { // 遍历物品
            for (int j = i * i; j <= n; j++) { // 遍历背包
                dp[j] = min(dp[j - i * i] + 1, dp[j]);
            }
        }
        return dp[n];
    }
};
// 版本一
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 0; i <= n; i++) { // 遍历背包
            for (int j = 1; j * j <= i; j++) { // 遍历物品
                dp[i] = min(dp[i - j * j] + 1, dp[i]);
            }
        }
        return dp[n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值