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

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

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

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

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

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

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

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

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

总代码如下:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        vector<int> dp(stones.size()*50+1,0);
        int sum=0;
        for(int i=0;i<stones.size();i++) sum+=stones[i];
        int target=sum/2;
        for(int i=0;i<stones.size();i++)
        {
            for(int j=target;j>=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.目标和

这道题还是很有难度的。 

首先,我们需要将这一个数组中的元素分为两堆,一堆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<int>& nums, int target) {
        int sum=0;
        for(int i=0;i<nums.size();i++) sum+=nums[i];
        if(abs(target)>sum) return 0;
        if((target+sum)%2==1) return 0;
        int left=(target+sum)/2;
        vector<int> dp(left+1,0);
        dp[0]=1;
        for(int i=0;i<nums.size();i++)
        {
            for(int j=left;j>=nums[i];j--)
            {
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[left];
     }
};

474.一和零

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

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

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

 这道题其实就是一个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小时,背包好难,一知半解。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值