day43|● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

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

1.代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for(int i: stones) {
            sum += i;
        }
        int t = sum;
        sum = sum /2;
        vector<int>f(sum + 1);
        for (int i = 0; i < stones.size(); i++) {
            for (int j = sum; j >= stones[i]; j--) {
                f[j] = max(f[j], f[j - stones[i]] + stones[i]); 
            }
        }
        return t - f[sum] - f[sum];
    }
};

2.思考

根据题目的意思,就是不断抵消石头,直到抵消为一个石头时停止,有很多种抵消石头的方法,求出最后石头重量最小的抵消石头的方法的石头重量。

如何求呢?我们可以把这些石头看成两堆,两堆相减就是最后一个石头的重量了,因为会不断抵消,可以自己模拟一下。

所以求出中间值,那个方法分出的更小的那一堆最接近中间值就是正确的

就可以转化成01背包问题:选几个物品,求出不超过体积的最大价值

这道题和切割等和子集不同的是这道题是最接近中间值而不是等于中间值

3.动规五部曲

先求出中间值sum = sum/2 , 不用管奇数和偶数

1,确定dp数组的含义

dp[i]相当于最大体积为i时的最大价值,这里是从数组中挑几个值,不超过i的情况下值的总和是多少

2.确定递推公式

当取每一个物品时可以表现为取它还是不取它,

不取它:dp[j] = dp[j]

取它: dp[j] = dp[j - stones[i]] + stones[i] |这里的空间和值是相同的,取这个石头必须要留足够存放这个石头的空间

3.初始化

当小于当前取得物品时就不能取了,需要为0, 可以在遍历顺序中判断

4.确定遍历顺序

第一层循环是取出每一个石头放到不同的背包中,直到把所有石头放完为止

第二层就是遍历所有可以放的下当前石头的背包,确定要不要放当前石头,逆序遍历防止重复放石头,因为会复用之前遍历的背包

5.模拟


494. 目标和

1.代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int i: nums) sum += i;
        //一共有nums.size个数,设加法总和为x, 减法的总和为 -(sum - x)
        if (abs(target) > sum) return 0;
        if ((target + sum) % 2 == 1) return 0;
        int x = (target + sum) / 2;
        vector<int>f(x + 1, 0);
        f[0]  = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = x; j >= nums[i]; j--) {
                f[j] += f[j - nums[i]];
            }
        }
        return f[x];
    }
};

2.思考

一个数组中每个元素可以变换符号,变换符号后每个元素相加等于目标值的个数有多少个,

可以看出一部分是正数,一部分是负数,但是正数和负数的值固定才能相加等于一个固定值,设正数值之和相加等于x,那么负数就能够确定为-{num-x},所以我们可以转化成,选出几个值相加,之和等于目标值的有多少个。可以用01背包求出

这是求个数

3.递归五部曲

1.确定dp数组和其含义

dp[i]代表相加后x等于j的次数

2.确定递推公式

因为加每一个物品时都有可能到达体积j,为了算出到达i的相加方案数,每次都需要加上f[j - nums[i]]这个方案数就能得到总方案数f[i] += f[i - nums[i]];

3.初始化

每次加不会加上而外的值,所以需要在前面初始化。几乎都是从f[0]得到的, f[0]代表到达0的方案数,所以为空集,f[0] = 1

4.遍历顺序

第一层是武品,第二层逆序背包

5.模拟


474. 一和零

1.代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>>f(m + 1, vector<int>(n + 1, 0));
        for (string str: strs) {
            int oneNum = 0;
            int zeroNum = 0;
            for (char ch: str) {
                if (ch == '0') zeroNum++;
                else oneNum++;
            }
            for (int i = m; i >= zeroNum; i--) {
                for (int j = n; j >= oneNum; j--) {
                    f[i][j] = max(f[i][j], f[i - zeroNum][j - oneNum] + 1);
                } 
            }
        }
        return f[m][n];

    }
};

 2.递归五部曲

1.确定dp数组和其含义

sp[i][j] 就是i个0和j个1的集合元素个数

2.确定递推公式

当每次拿物品时就会有一些0和一些1,要么选这个数要么不选就有两种方式

选f[i][j] = f[i - zeroNum][j - oneNum] + 1,增加一个元素

不选f[i][j] = f[i][j]

3.确定初始化

在遍历顺序中确定

4.遍历顺序

第一层就是遍历物品

第二层就是不超过总共的1和0的情况下遍历体积,

5.模拟

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值