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

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

题目链接:https://leetcode.cn/problems/last-stone-weight-ii/
文档讲解:https://programmercarl.com/1049.%E6%9C%80%E5%90%8E%E4%B8%80%E5%9D%97%E7%9F%B3%E5%A4%B4%E7%9A%84%E9%87%8D%E9%87%8FII.html
状态:需二刷,想了很久怎么转换为背包问题

思路:粉碎石头的过程本质上是为数组元素分配正号(+)和负号(-)的过程,最后一块石头的重量可以视为正数集合A - 负数集合B的结果

  • 公式推导:A + B = SUM,A = SUM - B,A - B = SUM - 2 * B
  • 目标:求解min(SUM - 2*B),即求解max(B),同时存在约束SUM - 2 * B >= 0,即最后一块石头的重量非负
  • 背包问题:背包容量SUM/2,物品是石头,物品价值和重量是石头重量,在不超过背包容量的前提下,求解最大价值max(B)

时间复杂度: O ( N ∗ M ) O(N*M) O(NM);空间复杂度: O ( N ∗ M ) O(N*M) O(NM)

// A+B = SUM
// A = SUM - B
// MIN(A-B) = MIN(SUM - 2B)
// 问题转化为求MAX(B),并且B <= SUM/2
// 背包容量为SUM/2,物品为数组中的元素,重量和价值为元素大小

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

        return sum - 2 * dp[n][target];
    }
}

494. 目标和(*)

题目链接:https://leetcode.cn/problems/target-sum/description/
文档讲解:https://programmercarl.com/0494.%E7%9B%AE%E6%A0%87%E5%92%8C.html
状态:需二刷,初始思路同时考虑正和负,没有通过拆解简化背包问题

思路

  • 公式推导:A+B = SUM,A-B = Target,可得A = (SUM + Target) / 2
  • 问题转换:背包容量为(SUM + Target) / 2,物品为数组元素,元素重量和价值为元素大小
  • 动态规划:dp[i][j]表示使用前i个物品,填满背包容量j的方法数量
    • d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − w [ i ] ] dp[i][j] = dp[i-1][j] + dp[i-1][j-w[i]] dp[i][j]=dp[i1][j]+dp[i1][jw[i]],根据是否选择第i个物品切分状态
    • 初始化:dp[0][0] = 1,注意如果有元素为0,dp[i][0]可能大于1

时间复杂度: O ( N ∗ M ) O(N*M) O(NM);空间复杂度: O ( N ∗ M ) O(N*M) O(NM)

// A+B = SUM
// A-B = Target
// A = (SUM + Target) / 2
// 背包容量为(SUM + Target) / 2
// dp[i][j]:使用前i个物品,填满背包容量j的做法数量
// dp[i][j] = dp[i-1][j] + dp[i-1][j-n[i]]
// dp[i][0] = 1

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int n = nums.length;
        int sum = 0;
        for(int i=0;i<n;i++)
            sum += nums[i];

        if((sum + target) % 2 == 1 || sum + target < 0)
            return 0;

        int m = (sum + target) / 2;
        int[][] dp = new int[n+1][m+1];
        dp[0][0] = 1;
        
        for(int i=1;i<=n;i++){
            for(int j=0;j<=m;j++){
                int tmpVal = j >= nums[i-1] ? dp[i-1][j-nums[i-1]] : 0;
                dp[i][j] = dp[i-1][j] + tmpVal;
            }
        }

        return dp[n][m];
    }
}

474. 一和零

题目链接:https://leetcode.cn/problems/ones-and-zeroes/description/
文档讲解:https://programmercarl.com/0474.%E4%B8%80%E5%92%8C%E9%9B%B6.html
状态:已完成

思路:由于有0和1两个指标,因此使用三维数组进行动态规划

  • d p [ i , j , k ] dp[i, j, k] dp[i,j,k]表示使用前i个元素,不超过j个0和k个1的前提下,最大子集长度
  • d p [ i , j , k ] = M a t h . m a x ( d p [ i − 1 , j , k ] , d p [ i − 1 , j − i 0 , k − i 1 ] + 1 ) dp[i, j, k] = Math.max(dp[i-1, j, k], dp[i-1, j-i_0, k-i_1]+1) dp[i,j,k]=Math.max(dp[i1,j,k],dp[i1,ji0,ki1]+1),其中 i 0 i_0 i0表示第i个元素中0的数量, i 1 i_1 i1同理
  • 初始化: d p [ 0 ] [ i ] [ j ] = 0 dp[0][i][j] = 0 dp[0][i][j]=0

时间复杂度: O ( L e n ∗ M ∗ N ) O(Len*M*N) O(LenMN);空间复杂度: O ( L e n ∗ M ∗ N ) O(Len*M*N) O(LenMN)

// dp[i][j][k]:前i个元素,满足j个0,k个1的最大子集长度
// dp[i, j, k] = Math.max(dp[i-1, j, k], dp[i-1, j-i0, k-i1]+1)
// 初始化:dp[i, 0, 0] = 1

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][][] dp = new int[len+1][m+1][n+1];
        
        for(int i=1;i<=len;i++){
            // 统计strs[i]中0和1的数量
            int num0 = 0;
            int num1 = 0;
            for(int j=0;j<strs[i-1].length();j++){
                if(strs[i-1].charAt(j) == '0')
                    num0++;
                else
                    num1++;
            }

            for(int j=0;j<=m;j++){
                for(int k=0;k<=n;k++){
                    int tmpVal = j >= num0 && k >= num1 ? dp[i-1][j-num0][k-num1]+1 : 0;
                    dp[i][j][k] = Math.max(dp[i-1][j][k], tmpVal);
                }
            }
        }

        return dp[len][m][n];
    }
}

你可能感兴趣的:(代码随想录算法训练营,算法)