代码随想录算法训练营第四十三天 | 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

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

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 24,得到 2,所以数组转化为 [2,7,1,8,1],
组合 78,得到 1,所以数组转化为 [2,1,1,1],
组合 21,得到 1,所以数组转化为 [1,1,1],
组合 11,得到 0,所以数组转化为 [1],这就是最优值。

可以将数组分为两边去推导,分别求得一遍最大的重量,令target=sum/2

动归五部曲:

  1. dp数组及其下标含义

dp[j]可以表示为容量为j,最多可以装的重量为[j]

其中题目重量为stones[i],价值也为stones[i]

  1. 确定递推公式

01背包问题的地推公式为:dp[j]=max(dp[j],dp[j-weight[i]]+value[i])

所以这道题的递推公式可以为:dp[j]=max(dp[j],dp[j-stones[i]]+stones[i])

  1. 确定递归方向

和昨天的分割子集问题一样,都是i从0-stones.length,而j反向推导,从target-stones[i]

  1. dp数组初始化

均初始化为0即可

  1. 距离推导dp数组

    stones=[2,4,1,1] target=4

    令i=0的时候 [0 0 2 2 2]

    令i=1的时候 [0 0 2 2 4]

    令i=2的时候 [0 1 2 3 4]

    令i=3的时候[0 1 2 3 4]

    dp[4]=4,sum-dp[4]-dp[4]=0

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum=0;
        for(int i:stones){
            sum+=i;
        }
        int target=sum/2;
        int[] dp=new int[target+1];
        for(int i=0;i<stones.length;i++){
            for(int j=target;j>=stones[i];j--){
                dp[j]=Math.max(dp[j],(dp[j-stones[i]]+stones[i]));
            }
        }
        return sum-2*dp[target];

    }
}

494. 目标和

给你一个整数数组 nums 和一个整数 target

向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

题目中一定存在left组合-right组合=target的情况

同时left+right=sum,其中sum是固定的

left=(target+sum)/2,其中target是固定的,sum是固定的,left就是我们需要求得可以当做bagsize

动规五部曲:

  1. 确定dp数组及其下标含义

dp[j]表示需要装满容量为j的背包有dp[j]种方法

  1. 确定递推公式

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

  1. dp数组初始化

需要将dp[0]=1不然后续累加出来的dp[j]全部都会为0

  1. 确定遍历方向

nums放在外循环,bagsize放在内循环倒序

  1. 举例推导dp数组

    nums=[1 1 1 1 1],target=3

    bagsize=(target+sum)/2=4

    当nums[0]时 [1 1 0 0 0]

    当nums[1]时 [1 2 1 0 0]

    当nums[2]时 [1 3 3 1 0]

    当nums[3]时 [1 4 6 4 1]

    当nums[4]时 [1 5 10 10 5]

    dp[4]=5;

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum=0;
        for(int i:nums) sum+=i;
        if(target<0&&sum<-target) return 0;
        if((target+sum)%2!=0) return 0;
        int size=(target+sum)/2;
        if(size<0) size=-size;
        int[] dp=new int[size+1];
        dp[0]=1;
        for(int i=0;i<nums.length;i++){
            for(int j=size;j>=nums[i];j--){
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[size];
    }
}

474. 一和零

给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

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

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5031 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"}{"10","1","0"}{"111001"} 不满足题意,因为它含 41 ,大于 n 的值 3
  1. 确定dp数组及其下标含义

dp[i][j]表示最多有i个0和j个i的strs的最大子集大小1为dp[i][j]

  1. 确定递推公式

dp[i][j]=Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);

  1. dp数组初始化

都初始化为0

  1. 确定遍历顺序

从后往前遍历

  1. 举例推导dp数组
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int[][] dp=new int[m+1][n+1];
        int oneNum;
        int zeroNum;
        for(String str:strs){
            oneNum=0;
            zeroNum=0;
            for(char ch:str.toCharArray()){
                if(ch=='0') zeroNum++;
                else oneNum++;
            }
            for(int i=m;i>=zeroNum;i--){
                for(int j=n;j>=oneNum;j--){
                    dp[i][j]=Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
                }
            }
        }
        return dp[m][n];

    }
}

你可能感兴趣的:(算法,动态规划)