代码随想录打卡—day43—【DP】— 8.27+28 01背包应用+总结

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

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

看了题解,理解一会儿,才知道为什么这题要转换成416. 分割等和子集很像AC代码:

cpp 复制代码
class Solution {
public:
    /* 一开始看到这题和416. 分割等和子集很像 真的不理解
    但是懒得严谨证明两堆石头重量差值最小就是目标对撞的两堆石头
    感觉是对的
    7,4  1,1,2,8
    2,7,1,1  4,8

    */
    int dp[1501]; // 容积的i的背包最多能装的物体的重量之和
    
    int lastStoneWeightII(vector<int>& stones) {
        dp[0] = 0;

        int total = 0;
        for(auto t : stones)total+=t;
        int sum = total;
        total /= 2;
        cout << sum << " " << total << endl;

        for(int i = 0; i < stones.size();i++)
        {
            for(int j = total; j >= 0; j--)
            {
                if(j < stones[i])dp[j] = dp[j];
                else dp[j] = max(dp[j],dp[j - stones[i]] + stones[i]);
            }
            for(int j = 0; j <= total; j++)cout << dp[j] << ' ';
            puts("");
        }
        return sum - dp[total] - dp[total];
    }
};

2 494. 目标和

494. 目标和

" 这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。本题则是装满有几种方法。其实这就是一个组合问题了。"

一开始的dp[]数组含义写错了,后来学习题解和视频,才学习到dp[]数组的含义,然后发现这个转移方程比较难理解,和之前学过的不大一样。**大概理解了88%,下次再做一次看懂了没有。**详细解释见注释,AC代码:


组合类问题 的公式,在求装满背包有几种方法的情况下,递推公式一般为:

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

这个公式在后面在讲解背包解决排列组合问题的时候还会用到!

cpp 复制代码
class Solution {
public:
    /*
        物体:nums个数,01表示每个整数前面添加+还是负
        价值:nums[i] 重量:+- nums[i]
    */

    int dp[2010];  // 容量是 j 的背包 装满时候不同的方式
    /*
        我一开始的写法:
        "容积为 i 的背包能够正好装nums个数且重量正好是i的不同组合的最大值"
        但这样的问题是没有转换位01背包 即这些物体是可以分成2堆的
        本题的思路:
        设一个left集合装nums中所有+数,right装nums中所有-数
        left.sum-right.sum = target
        left.sum+righr.sum = sum
        所以,left.sum = ( target + sum ) / 2
        一个背包容量是( target + sum ) / 2的背包设只装正数
        求装满的不同方式有多少种
    */

    /*
        状态转移:(我直接抄的,和之前做过的都不太一样)
        dp[j] += dp[j - nums[i]]
        初始化:
        dp[0] = 1(凑的 没有实际含义)
        顺序:
        for(int i = 0; i < nums.size();i++)
        {
            for(int j = ( target + sum ) / 2; j >= 0; j--)
            {
            }
        }
        模拟:
        1 1 0 0 0 
        1 2 1 0 0 
        1 3 3 1 0 
        1 4 6 4 1 
        1 5 10 10 5 
    */

    int findTargetSumWays(vector<int>& nums, int target) 
    {
        dp[0] = 1;

        int sum = 0;
        for(auto t : nums)sum += t;

        if((target+sum) % 2 != 0)return 0;

        for(int i = 0; i < nums.size();i++)
        {
            for(int j = ( target + sum ) / 2; j >= 0; j--)
            {
                if(j >= nums[i])dp[j] += dp[j - nums[i]];
            }
            for(int j = 0; j <= ( target + sum ) / 2; j++)
                cout << dp[j] << ' ';
            puts("");
        }
        return dp[( target + sum ) / 2];
    }
};

3 474. 一和零

474. 一和零

本题两个地方卡住,看了这两个地方的题解tips,独立写完,AC代码:

  1. 一个字符串数组中分成2堆,一堆中有n个1,有m个0

一个m一个n怎么在一个背包中体现?

【【看了提示1】】题解tips:用两个背包====》二维dp背包
2. 遍历m*n的二维数组时候

// for(int i = 0; i <= n; i++) 错误!!!! 【【看了提示2】】

for(int i = n; i >= 0; i--)

i-- 而不能是 i++ 的 原因是:dp[i][j] = max(dp[i-a][j-b] + 1 , dp[i][j]); 这里的 i-a 和 j-b 暗含了对于当前 i,j,左上方的这些数都要是k-1次的即上一次的。

cpp 复制代码
class Solution {
public:
    /*
        转换为01背包
        一个字符串数组中分成2堆,一堆中有n个1,有m个0
        一个m一个n怎么在一个背包中体现?

        【【看了提示1】】题解tips:用两个背包====》二维背包
        dp[n][m] (i,j)能装i个1和能装j个0的背包装满时候 的最大物体个数
    */

    int dp[110][110]; //(i,j)能装i个1和能装j个0的背包装满时候的最大物体个数

    /*
        转换方程:
        设当前k物体有a个1和b个0
        dp[i][j] = max(dp[i-a,j-b]+1,dp[i,j])

        初始化
        dp[0][0] = 0 其他都是0

        顺序:
        for(int k = 0; k < str.size();k++)
        {
            for(int i = 0; i < n; i++)  错误!应该i--
            {
                for(int j = 0; j < m; j++)错误!应该j--
                {

                }
            }
        }
        
        模拟:
        ------
    */


    int findMaxForm(vector<string>& strs, int m, int n) 
    {
        dp[0][0] = 0;

        for(int k = 0; k < strs.size();k++)
        {

            int a = 0;
            int b = 0;
            for(int i = 0; i < strs[k].size(); i++)
            {
                if(strs[k][i] == '1')a++;
                else b++;
            }

            // for(int i = 0; i <= n; i++)  【【看了提示2】】
            for(int i = n; i >= 0; i--)
            {
                // for(int j = 0; j <= m; j++)
                for(int j = m; j >= 0; j--)
                {
                    if(i >= a && j >= b)dp[i][j] = max(dp[i-a][j-b] + 1 , dp[i][j]);
                    else dp[i][j] = dp[i][j];
                }
            }

            // for(int i = 0; i <= n; i++)
            // {
            //     for(int j = 0; j <= m; j++)
            //     {
            //         cout << dp[i][j] << ' ';
            //     }
            //     cout << endl;
            // }
            // cout << endl;

        }
        return dp[n][m];
    }
};

总结:01背包总结

总结前几个刷过的01背包的应用:

纯01背包:求给定背包容量,装满一个背包,里面能装物品的最大价值总和是多少。

416. 分割等和子集:求给定背包容量,能否装满一个背包

1049. 最后一块石头的重量 II:求给定背包容量,在不超限情况下,背包能够装的最大的重量。

494. 目标和:求给定背包容量,装满背包有多少种方式。

474. 一和零:求给定背包容量,装满背包最多有多少个物品。

相关推荐
sp_fyf_202420 分钟前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
我是哈哈hh2 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
Tisfy2 小时前
LeetCode 2187.完成旅途的最少时间:二分查找
算法·leetcode·二分查找·题解·二分
Mephisto.java2 小时前
【力扣 | SQL题 | 每日四题】力扣2082, 2084, 2072, 2112, 180
sql·算法·leetcode
robin_suli2 小时前
滑动窗口->dd爱框框
算法
丶Darling.2 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5203 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法
jiyisuifeng19913 小时前
代码随想录训练营第54天|单调栈+双指针
数据结构·算法
꧁༺❀氯ྀൢ躅ྀൢ❀༻꧂3 小时前
实验4 循环结构
c语言·算法·基础题
新晓·故知3 小时前
<基于递归实现线索二叉树的构造及遍历算法探讨>
数据结构·经验分享·笔记·算法·链表