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

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

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

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

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]]

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

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次的即上一次的。

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. 一和零:求给定背包容量,装满背包最多有多少个物品。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值