代码随想录训练营Day42 动态规划 part05● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

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

题目链接:1049. 最后一块石头的重量 II - 力扣(LeetCode)

文章链接:代码随想录 (programmercarl.com)

视频链接:​​​​​​这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II

代码随想录训练营Day42 动态规划 part05● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零_第1张图片

 这道题其实也是个背包问题,不过要稍微动一动脑筋。怎样使剩下的石头最小?我们可以把这一堆石头尽量分成重量相近的两堆,那只要这两堆相减,就得到了最小的石头了。

这样的话,这道题就与昨天的分割等和子集一个道理了。

将石头的重量和价值都用它给我们的stones数组表示。

然后通过动规五部曲分析(详见Day41)

总代码如下:

class Solution {
public:
    int lastStoneWeightII(vector& stones) {
        vector dp(stones.size()*50+1,0);
        int sum=0;
        for(int i=0;i=stones[i];j--)
            {
                dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }
        return sum-2*+dp[target];
    }
};

494. 目标和

 题目链接:494. 目标和 - 力扣(LeetCode)

文章链接:代码随想录 (programmercarl.com)

视频链接:​​​​​​装满背包有多少种方法?| LeetCode:494.目标和

代码随想录训练营Day42 动态规划 part05● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零_第2张图片

这道题还是很有难度的。 

首先,我们需要将这一个数组中的元素分为两堆,一堆left前面加‘+’,一堆right前面加‘-’,只要right堆-left堆=target就行了。其实就有了一个方程组。

right-left=target;

right+left=sum;

解方程组可得:left=(target+sum)/2;

那么,我们就将问题转化为求能组成left堆元素总和的方法个数。

动规五部曲:

1.确认dp数组及下标含义。

dp[j]表示填满容量为j的背包有dp[j]种填法。那么我们最后就是要求dp[left]。

2.确认递推函数

填满容量背包为j的背包,有几种填法?

我们假设背包容量为5.

递推函数永远与它的上一状态有关。

假如它的上一状态已经填了1容量,那么还剩4容量,那么就是还要dp[4]。

已经填了2,那么还需要dp[3]种方法

已经填了3,那么还需要dp[2]种方法

已经填了4,那么还需要dp[1]种方法

已经填了5,那么还需要dp[0]种方法

那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。

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

3.dp数组初始化。

通过递归公式可知,j永远与j-nums[i]有关,dp[0]是在公式中一切递推结果的起源,背包为0填满需要几种方法,答案是一种,因为没有方法也是一种方法。

dp[0]=1;

4.确认遍历顺序

滚动数组我,们讲过对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。

5.举例推导dp数组。

总代码如下:

class Solution {
public:
    int findTargetSumWays(vector& nums, int target) {
        int sum=0;
        for(int i=0;isum) return 0;
        if((target+sum)%2==1) return 0;
        int left=(target+sum)/2;
        vector dp(left+1,0);
        dp[0]=1;
        for(int i=0;i=nums[i];j--)
            {
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[left];
     }
};

474.一和零

 题目链接:474. 一和零 - 力扣(LeetCode)

文章链接:代码随想录 (programmercarl.com)

视频链接:​​​​​​装满这个背包最多用多少个物品?| LeetCode:474.一和零

代码随想录训练营Day42 动态规划 part05● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零_第3张图片

 这道题其实就是一个01背包问题,只不过有两个维度而已,0的个数和1的个数,所以就需要构建二维dp数组。

动规五部曲:

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,保证递推的时候dp[i][j]不会被初始值覆盖。

4.确认遍历顺序。

外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

5.举例推导dp数组

总代码如下:

for (string str : strs) { // 遍历物品
    int oneNum = 0, zeroNum = 0;
    for (char c : str) {
        if (c == '0') zeroNum++;
        else oneNum++;
    }
    for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
        for (int j = n; j >= oneNum; j--) {
            dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
        }
    }
}

Day42打卡成功,耗时3.5小时,背包好难,一知半解。

你可能感兴趣的:(动态规划,算法,leetcode,c++,数据结构)