代码随想录学习Day 37

1049.最后一块石头的重量Ⅱ

题目链接

讲解链接

思路:与前一题基本一致,将所有石头分为重量最接近sum/2的两堆,碰撞之后剩下的就是最小值

动规五部曲:
1.dp数组及其下标含义:重量为j的背包所能容纳的最大价值(本题中也就是重量)为dp[j];

2.确定递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

3.dp数组初始化:初始化全为0即可;

4.确定遍历顺序:先遍历石头,再遍历背包;

5.打印dp数组:当stones = [2,7,4,1,8,1]时,dp = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]。

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        target = sum(stones) // 2  # 分割值为总和的一半
        dp = [0] * (target + 1)  # 初始化长度为target + 1的全零dp数组
        for i in stones:  # 先遍历石头
            for j in range(target, i - 1, -1):  # 再倒序遍历背包
                dp[j] = max(dp[j], dp[j - i] + i)
        return sum(stones) - 2 * dp[-1]  # 返回碰撞后所剩的值

494.目标和

题目链接

讲解链接

本题类似于回溯算法中的组合总和问题,代码如下:

class Solution:
    def backtracking(self, candidates, target, total, startIndex, path, result):
        if total == target:  # 回溯终止条件
            result.append(path[:])
        for i in range(startIndex, len(candidates)):
            if total + candidates[i] > target:  # 大于目标值则直接跳出循环
                break
            total += candidates[i]
            path.append(candidates[i])
            self.backtracking(candidates, target, total, i + 1, path, result)
            total -= candidates[i]
            path.pop()
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total = sum(nums)
        if target > total:  # 目标和大于总和说明无解
            return 0
        if (target + total) % 2 != 0:  # 目标和与总和加起来的值必须是偶数才有解,否则无解,例如目标和为3,总和为5,这时和为8,有解,若目标和为4,则无解,因为5个1不可能求出4
            return 0
        bagSize = (target + total) // 2  # 将问题转化为组合总和问题,bagSize就是新的目标和
        result = []
        nums.sort()
        self.backtracking(nums, bagSize, 0, 0, [], result)
        return len(result)

动态规划思路:假设加法的总和(nums中取加号的数)为add,减法的总和(nums中取减号的数)为sub,所以 add + sub  = sum,add - sub = target,那么:

sub = sum - add → add - sub  = (sum - add)  = target → add = (target + sum) / 2

此时问题就转化为,装满容量为 add 的背包,有几种方法

1.dp数组及其下标含义:装满容量为j的背包有dp[j]种方法; 

2.确定递推公式:已知nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。所以递推公式为:

dp[j] += dp[j - nums[i]];

3.dp数组初始化:从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

4.确定遍历顺序:nums放在外循环,target在内循环,且内循环倒序,也就是先物品后背包。

5.打印dp数组

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        if abs(target) > sum(nums):  # 目标绝对值大于总和说明无解
            return 0
        if (target + sum(nums)) % 2 != 0:  # 不是偶数说明无解
            return 0
        target_sum = (target + sum(nums)) // 2  # 求出目标和(代表背包总容量)
        dp = [0] * (target_sum + 1)
        dp[0] = 1  # j等于0时初始化为1
        for i in nums:  # 先物品(数)
            for j in range(target_sum, i - 1, -1):  后背包(目标和)
                dp[j] += dp[j - i]  # 递推公式
        return dp[-1]

474.一和零

题目链接

讲解链接

本题中strs数组里的元素就是物品,不同长度的字符串就是不同大小的待装物品。而m和n相当于是一个拥有两个维度的背包

动规五部曲:

1.dp数组及其下标含义:dp[i][j]指最多有i个0和j个1的strs的最大子集的大小为dp[i][j];

2.确定递推公式:dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。然后我们在遍历的过程中,取dp[i][j]的最大值。所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);

3.dp数组初始化:全部初始化为0;

4.确定遍历顺序:先物品(字符串)后背包(两个维度)

5.打印dp数组

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for str in strs:
            zero = str.count('0')
            one = str.count('1')
            for i in range(m, zero - 1, -1):
                for j in range(n, one - 1, -1):
                    dp[i][j] = max(dp[i][j], dp[i - zero][j - one] + 1)
        return dp[-1][-1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值