代码随想录Day34-动态规划:力扣第474m、518m、377m题

474m. 一和零

题目链接
代码随想录文章讲解链接

方法一:动态规划-01背包

用时:43m57s

思路

相比于之前的01背包问题多了一个维度,之前的背包是只有一个容量限制,本题有两个容量限制,分别是0的容量限制和1的容量限制。
dp数组:dp[j][k]表示:对于0到i的字符串,0的容量为j、1的容量为k的背包能装的字符串的最大数量
状态转移方程:dp[j][k] = max(dp[j][k], dp[j - num0][k - num1] + 1),当前字符串要么装要么不装。

  • 时间复杂度: O ( l m n ) O(lmn) O(lmn) l l l是字符串数组的长度。
  • 空间复杂度: O ( m n ) O(mn) O(mn)
C++代码
class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        for (string& s : strs) {
            // 统计当前字符串中0和1的数量
            int num0 = 0, num1 = 0;
            for (char& c : s) {
                if (c == '0') ++num0;
                else ++num1;
            }
            // dp
            for (int j = m; j >= num0; -- j) {
                for (int k = n; k >= num1; --k) {
                    dp[j][k] = max(dp[j][k], dp[j - num0][k - num1] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

看完讲解的思考

多维度的01背包问题,一开始是想把多个维度拆分成多个01背包,还是搞复杂了。

代码实现遇到的问题

无。


完全背包

题目描述

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解物品装入背包里的最大价值。

示例:
输入:w = 4, weight = [1, 3, 4], value = [15, 20, 30]
输出:60
解释:放入4个索引为0的物品,最大价值为15*4=60。

代码随想录文章讲解链接

方法一:动态规划

思路

dp数组:二维dp数组,dp[i][j]表示对于物品0到i,容量为j的背包可以装的最大价值
状态转移:dp[i][j] = max(dp[i][j - 1], dp[i][j - weight[i]] + value[i]),注意关键在于max函数的第二项,在01背包中是为dp[i - 1][j - weight[i]] + value[i],因为01背包不能重复放,所以是i - 1,而完全背包可以重复放,所以是i

  • 时间复杂度: O ( w ∗ n ) O(w*n) O(wn)
  • 空间复杂度: O ( w ∗ n ) O(w*n) O(wn)
C++代码
class Solution {
public:
    int completePack(int w, vector<int>& weight, vector<int>& value) {
        int size = weight.size();
		vector<vector<int>> dp(size, vector<int>(w + 1, 0));

        // 初始化dp数组
        for (int j = 1; j <= w; ++j) {
            if (weight[0] <= j) dp[0][j] = max(dp[0][j - 1], dp[0][j - weight[0]] + value[0]);
        }
        // dp
        for (int i = 1; i < size; ++i) {
            for (int j = 1; j <= w; ++j) {
                if (weight[i] > j) dp[i][j] = dp[i - 1][j];  // 如果物品i的重量大于j,那么物品i肯定不放,所以dp[i][j]就等于dp[i - 1][j]
                else dp[i][j] = max(dp[i][j - 1], dp[i][j - weight[i]] + value[i]);
            }
        }
        return dp[size - 1][w];
    }
};

方法二:动态规划+滚动数组

思路

关键在于正序遍历,正序遍历相当于同一个物品可以放置多次。

  • 时间复杂度: O ( w ∗ n ) O(w*n) O(wn)
  • 空间复杂度: O ( w ) O(w) O(w)
C++代码
int completePack(int w, vector<int>& weight, vector<int>& value) {
        int size = weight.size();
		vector<int> dp(w + 1, 0);

        for (int i = 0; i < size; ++i) {
            for (int j = weight[i]; j <= w; ++j) {
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[w];
    }

看完讲解的思考

无。

代码实现遇到的问题

无。


518m. 零钱兑换II

题目链接
代码随想录文章讲解链接

方法一:动态规划-完全背包

用时:21m26s

思路

dp数组:二维dp数组,dp[i][j]表示使用硬币0到i组合成数额j的组合数量
状态转移:如果硬币i大于数额j,那么硬币i无法使用,组合数量就相当于不使用硬币i的组合数量dp[i][j] = dp[i - 1][j]。如果硬币i小于等于数额j,硬币i可以使用,组合数量等于不使用硬币i的组合数加上使用了硬币i的组合数,不使用硬币i的组合数为dp[i - 1][j],使用硬币i的组合数为dp[i][j - coins[i]],则dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]]

  • 时间复杂度: O ( n ∗ a m o u n t ) O(n*amount) O(namount)
  • 空间复杂度: O ( n ∗ a m o u n t ) O(n*amount) O(namount)
C++代码
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int size = coins.size();
        vector<vector<int>> dp(size, vector<int>(amount + 1, 0));

        // 初始化dp数组
        for (int i = 0; i < size; ++i) dp[i][0] = 1;
        for (int j = 1; j <= amount; ++j) {
            if (coins[0] <= j) dp[0][j] = dp[0][j - coins[0]];
        }
        // dp
        for (int i = 1; i < size; ++i) {
            for (int j = 1; j <= amount; ++j) {
                if (coins[i] > j) dp[i][j] = dp[i - 1][j];
                else dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
            }
        }
        return dp[size - 1][amount];
    }
};

方法二:动态规划-完全背包+滚动数组

用时:1m30s

思路
  • 时间复杂度: O ( n ∗ a m o u n t ) O(n*amount) O(namount)
  • 空间复杂度: O ( a m o u n t ) O(amount) O(amount)
C++代码
class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int size = coins.size();
        vector<int> dp(amount + 1, 0);

        // 初始化dp数组
        dp[0] = 1;
        // dp
        for (int i = 0; i < size; ++i) {
            for (int j = coins[i]; j <= amount; ++j) dp[j] = dp[j] + dp[j - coins[i]];
        }
        return dp[amount];
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


377m. 组合总和 Ⅳ

题目链接
代码随想录文章讲解链接

方法一:动态规划-完全背包

用时:14m30s

思路

**关键点:**外层循环是遍历背包容量,内层循环是遍历数组。
内层循环遍历数组,这样就能计算上以每个元素为开头的排列数。
如果外层遍历数组,内层遍历背包,这样每次只能计算以nums[i]作为开头的排列数。

  • 时间复杂度: O ( n ∗ t a r g e t ) O(n*target) O(ntarget)
  • 空间复杂度: O ( t a r g e t ) O(target) O(target)
C++代码
class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        int size = nums.size();
        vector<int> dp(target + 1, 0);
        dp[0] = 1;
        for (int j = 1; j <= target; ++j) {
            for (int i = 0; i < size; ++i) {
                if (nums[i] <= j && dp[j - nums[i]] < INT_MAX - dp[j]) dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

看完讲解的思考

完全背包中,遍历的顺序会导致不同的结果。

代码实现遇到的问题

无。


爬楼梯进阶版(改自70. 爬楼梯)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 [1, 2, …, n] 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

示例 1
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶

示例 2
输入: 3
输出: 4
解释: 有四种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
3 阶

代码随想录文章讲解链接

方法一:动态规划-完全背包

思路
  • 时间复杂度: O ( ) O() O()
  • 空间复杂度: O ( ) O() O()
C++代码
class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        for (int j = 0; j <= n; ++j) {
        	for (int i = 1; i <= j; ++i) dp[j] += dp[j - i];
        }
        return dp[n];
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


最后的碎碎念

你的背包~ 背到现在还没烂~
背包问题还是有难度的,搞懂了01背包,又来完全背包。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值