LeetCode动态规划经典题目(九):0-1背包拓展

目录

11. LeetCode494. 目标和

12. LeetCode474. 一和零

13. 完全背包

14. LeetCode518. 零钱兑换 II


11. LeetCode494. 目标和

思路:
本题可分成两个组合,plus和minus,一个加正号,另一个加负号。
plus-minus=target,plus+minus=sum=>plus=(target+sum)/2
因此问题转变成在集合nums中找出和为plus的组合,即装满容量为(target+sum)/2的背包。把二维标准(+和-)转换成唯一标准(+),让集合nums里的元素组成我们的newTarget

1.注意特殊情况:
  1.1如果target+sum是奇数,(target+sum)/2无法精准取到,所以无解,return 0
  1.2如果target的绝对值大于sum,无论如何都无法组合成target,也是无解
2.因为每个物品(数字)只能用一次,所以是0-1背包问题,之前是装满的最大价值,但是现在求的是有几种方法装满背包。
3.dp[i][j]:使用下标为[0,i]的数字装满容量为j的背包的方法数
4.dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]]:(j-nums[i]+nums[i]=j)
5.初始化dp:dp[0][0]=1,因为dp[0][0]是一切递推结果的起源

滚动数组:(如果用二维表需要考虑太多没必要的情况,防止堆溢出)
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //分成plus和minus
        //plus-minus=target,plus+minus=sum=>new target=plus=(sum+target)/2
        
        int sum=0;//nums总和
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }

        //特殊情况
        if((sum+target)%2==1||abs(target)>sum)return 0;
        int newTarget=(sum+target)/2;

        //dp[j]:装满容量为j的背包的方法数
        vector<int>dp(newTarget+1);
        //初始化dp
        dp[0]=1;
        //完善dp
        for(int i=0;i<nums.size();i++){
            for(int j=newTarget;j>=nums[i];j--){
                dp[j]=dp[j]+dp[j-nums[i]];//取或不取的方法总数
            }
        }
        return dp[newTarget];
    }
};

12. LeetCode474. 一和零

1.dp[i][j]:最多有i个0和j个1的最大子集大小
2.dp[i][j]=max(dp[i][j],dp[i-count0][j-count1]+1)
3.初始化dp
4.遍历顺序:外层遍历物品(strs),内层遍历背包容量(count0,count1)

滚动二维表:
class Solution {
public:
    pair<int,int>getZeroOne(string& str){
        int count0=0;
        int count1=0;
        for(int i=0;i<str.length();i++){
            if(str[i]=='0')count0++;
            else count1++;
        }
        return {count0,count1};
    }

    int findMaxForm(vector<string>& strs, int m, int n) {
        //dp[i][j]:最多有i个0和j个1的最大子集大小
        vector<vector<int>>dp(m+1,vector<int>(n+1));
        //初始化dp,初始全为0即可
        //完善dp
        for(string&str:strs){//遍历物品
            pair<int,int>data=getZeroOne(str);
            for(int i=m;i>=data.first;i--){
                for(int j=n;j>=data.second;j--){//从后向前遍历背包容量
                    dp[i][j]=max(dp[i][j],dp[i-data.first][j-data.second]+1);
                }
            }
        }
        return dp[m][n];
    }
};

13. 完全背包

完全背包:有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包与0-1背包唯一不同的是:完全背包每件物品有无限件。

14. LeetCode518. 零钱兑换 II

    1.dp[j]:组成j的组合数
    2.dp[j]+=dp[j-coins[i]]
    3.初始化dp:dp[0]=1;//只有不取coins[0],才能组成0
    4.遍历顺序:先遍历物品,在遍历容量

    滚动数组:
    class Solution {
    public:
        int change(int amount, vector<int>& coins) {
            //dp[j]:拼成j的组合数
            vector<int>dp(amount+1);
            //初始化dp
            dp[0]=1;
            //完善dp
            for(int i=0;i<coins.size();i++){//遍历物品
                for(int j=coins[i];j<=amount;j++){//遍历背包
                    //假设在二维表中,取n个coins[i]组成j的方法数由n-1个coins[i]递推而得
                    //dp[i][j]需要用到同一行的dp值,因此在滚动数组中,先更新前面再由前面推得后面,使后面的dp值更新
                    //组成j:既可以不取coins[i](dp[j]),也可以比前一次多取一个coins[i](dp[j-coins[i]])
                    dp[j]+=dp[j-coins[i]];//因为是求方法数,所以要累加起来
                }
            }
            return dp[amount];
        }
    };

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值