day42 分割等和子集 最后一块石头的重量 目标和 一和零

题目1:416 分割等和子集

题目链接:416 分割等和子集

题意

将包含正整数的非空数组分割成两个子集,使得两个子集中的元素和相等,如果可以返回true,否则返回false

数组中的元素小于等于100 数组中元素个数小于等于200  所以dp数组内部是0~10000

每个元素只可使用1次 本题转化为01背包问题     数组中的元素能否装满容量为sum/2的背包     重量和价值等价

dp[sum/2] == (sum/2)  

动态规划

1)明确dp数组及下标j的含义

dp[j]   容量为j的背包所能装的最大价值

2)dp数组初始化

容量为0的背包所能装的最大价值   dp[0] = 0  

其他非零容量的背包所能装的最大价值  dp[j] = 0  根据递推公式,将其设为非负整数的最小值0

3)递推公式

weight[i] == value[i] == nums[i]

dp[j] = max(dp[j],dp[j-numst[i]]+nums[i])

4)遍历顺序

正序遍历物品,倒序遍历背包

正序遍历nums, 倒序遍历nums

for(int i=0;i<nums.size();i++){

       for(int j=(sum/2);j>=nums[i];j--){

}}

5)打印dp数组

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        //定义dp数组 初始化dp数组
        vector<int> dp(10001, 0);
        int sum = 0;
        for(int i=0;i<nums.size();i++){
            sum += nums[i];
        }
        //剪枝
        if(sum % 2)  return false;
        int target = sum / 2;
        //遍历顺序
        //先遍历物品,后遍历倒序背包
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[target] == target) return true;
        return false;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n),虽然dp数组大小为一个常数,但是大常数

题目2:1049 最后一块石头的重量Ⅱ

题目链接:1049 最后一块石头的重量Ⅱ

题意

整数数组元素stones[i]表示第i块石头的重量

每次任选2块石头粉碎      两石头的重量  x<=y

当x==y 两块石头被完全粉碎;       当x!=y 重量为x的石头会完全粉碎,重量为y的石头新重量是y-x

最多只会剩一块石头,返回此石头的最小可能重量;若无石头剩下,就返回0

数组中的元素小于等于100 数组中元素个数小于等于30  所以dp数组内部是0~1500

每块石头只使用1次,01背包问题  本题和上一题分割等和子集是一个求法,也是把数组分成两组,让两组尽可能地相等,这样取得的差最小  求得装满背包容量为(sum/2)的背包的最大价值

最终求 sum-dp[sum/2]-dp[sum/2]即可

动态规划

1)明确dp数组及下标j的含义

dp[j]   装满容量为j的背包,最多装的重量是dp[j]

2)dp数组初始化

背包容量为0时,dp[0] = 0

背包容量不为0时,更加递推公式,初始化dp[j]为0    dp[j]=0

3)递推公式

dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])

4)遍历顺序

先遍历物品  后倒序遍历背包

for(int i=0;i<stones.size();i++){

for(int j=(sum/2);j>=stones[i];j--){

}}

5)打印dp数组

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        //定义dp数组  初始化dp数组
        vector<int> dp(1501,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]);
            }
        }
        int ret = sum - dp[target];
        if(ret-dp[target]) return abs(ret-dp[target]);
        return 0;
    }
};
  • 时间复杂度:O(n × m) , n为石头块数    m是石头总重量的一半,
  • 空间复杂度:O(m)

题目3:494 目标和

题目链接:494 目标和

题意

非负整数数组nums 在数组中的每个整数前添加‘+’ ‘-’串联起所有整数,构造一个表达式

返回等于表达式结果等于target的不同表达式的数目

分成两组,一组是加法的集和left,一组是减法的集和right

left+right=sum ①

left-right=target ②

由①得 right=sum-left ③

将③代入②得 left-(sum-left)=target   

式中只有1个变量left   left=(target+sum)/2     找出元素和组成为left的情况dp[left]  每个元素只能使用1次  01背包   装满容量为left的背包有几种方法

如果(sum+target)不能被2整除,那么就不能找到一个left (理由:将①式和②式相加,得2*left=sum+target   要想找到一个left,(sum+target)一定要被整除)

还有就是  abs(target)>sum  避免出现数组中只有1个元素,而该元素的绝对值还小于abs(target)的情况    例如 nums=[100]  target=-200

动态规划

1)明确dp数组及下标i的含义

dp[j] 找出装满背包容量为j的背包,有dp[j]种方法

2)dp数组初始化

装满容量为0的背包,有dp[0]种方法     dp[0]=1

3)递推公式

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

4)遍历顺序

先正序遍历物品,后倒序遍历背包

for(int i=0;i<nums.size();i++){

       for(int j=left;j>=nums[i];j--){

}}

5)打印dp数组

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //定义dp数组
        vector<int> dp(20001, 0);
        //初始化dp数组
        dp[0] = 1;
        int sum = 0;
        for(int i=0;i<nums.size();i++){
            sum += nums[i];
        }
        if((sum + target) % 2) return 0;
        if(abs(target) > sum) return 0;
        int left = (sum + target) / 2;
        //遍历顺序
        //先遍历物品,后倒序遍历背包
        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];

    }
};
  • 时间复杂度:O(n × m),n为数组中元素的个数,m为背包容量
  • 空间复杂度:O(m),m为背包容量

题目4:474 一和零

题目链接:474 一和零

题意

返回二进制字符串数组strs 的最大子集长度,子集中最多有m个0和n个1

若x的所有元素也是y的元素,集合x是集合y的子集

两个维度(0   1)的背包且每个元素只能使用1次   01背包   二维dp数组   dp[m][n]       物品的重量就是x个0  y个1

动态规划

1)明确dp数组及下标j的含义

dp[i][j]   装满i个0和j个1最多背了dp[i][j]个物品

2)dp数组初始化

背包容量为00时,不装东西   dp[0][0]=0

背包容量不为0时,根据递推公式,dp[i][j]=0

3)递推公式

装x个0  y个1  将物品放进来(个数+1)

dp[i][j] = max(dp[i][j], dp[i-x][j-y]+1)

4)遍历顺序

先遍历物品,再倒序遍历背包

for(string str:strs){//遍历物品

//统计该字符串的0的个数和1的个数

   int x,y=0;

   for(char c:str){

      if(c=='0') x++;

      if(c=='1') y++;}

   for(int i=m;i>=x;i--){//遍历背包容量

      for(j=n;j>=y;j--){

      }

   }

  }

}

5)打印dp数组

代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        //定义dp数组  初始化dp数组
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        //遍历顺序
        //先遍历物品,后倒序遍历背包
        for(string str : strs){//遍历物品
            int x = 0;
            int y = 0;
            //获取0和1的个数
            for(char c : str){
                if(c == '0') x++;
                if(c == '1') y++;
            }
            //倒序遍历背包
            for(int i=m;i>=x;i--){
                for(int j=n;j>=y;j--){
                    dp[i][j] = max(dp[i][j], dp[i-x][j-y]+1);
                }
            }
        }
        return dp[m][n];
    }
};
  • 时间复杂度: O(kmn),k 为strs的长度
  • 空间复杂度: O(mn)
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值