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

104 阅读3分钟

一、题目描述

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

有一堆石头,每块石头的重量都是正整数。每组选出两块石头,一组是 xx,一组是 yy,假设 xyx\leq y,则重量为 (yx)(y-x) 的石头会留下来。现在从这堆石头中选出一些石头使得留下来的石头的总重量最小,求最小的总重量。

  1. 目标和

给定一个整数数组 numsnums 和一个整数 targettarget,在数组中找出能够凑成 targettarget 的元素组合,其中每个元素都可以被选或者不选,问有多少种不同的组合方式。

  1. 一和零

给定 mm00nn11,现在从中选出若干个数字,使得选出的数字中 00 的个数为 kk11 的个数为 ll,求最多可以选出多少个数字。

二、解题思路

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

这道题可以转化为一个背包问题。假设集合 SS 中所有石头的重量和为 sumsum,则可以将这些石头分成两组 AABB,满足 AA 中的石头的重量和尽可能接近 sum2\frac{sum}{2},即 aAasum2\sum_{a\in A}a\approx\frac{sum}{2}。由于 AABB 的石头的重量和之和为 sumsum,因此 BB 中石头的重量和尽可能接近 sum2\frac{sum}{2}。所以问题转化为求在重量和不超过 sum2\frac{sum}{2} 的情况下,选出的石头的重量和最大。

使用 0-1 背包算法即可解决,时间复杂度为 O(n)O(n\sum),其中 nn 为石头的数量,\sum 为所有石头的重量和。

  1. 目标和

将这个问题转化为一个背包问题。假设集合 SS 中所有数字的和为 sumsum,则可以将这些数字分成两组 AABB,满足 AA 中的数字的和为 targettarget,即 aAa=target\sum_{a\in A}a=target。由于 AABB 的数字的和之和为 sumsum,因此 BB 中数字的和为 sumtargetsum-target。所以问题转化为在 SS 中选出若干个数字,使得这些数字的和为 sumtargetsum-target

使用 0-1 背包算法即可解决,时间复杂度为 O(n)O(n\sum),其中 nn 为数字的数量,\sum 为所有数字的和。

  1. 一和零

将这个问题转化为一个背包问题。假设集合 SS 中所有数字的个数为 nn,其中 00 的个数为 m0m_011 的个数为 m1m_1,则可以将这些数字分成两组 AABB,满足 AA 中的数字中 00 的个数为 kk11 的个数为 ll,即 aAa0=k,aAa1=l\sum_{a\in A}a_0=k,\sum_{a\in A}a_1=l。由于 AABB 的数字个数之和为 nn,因此 BB 中数字的个数为 m0km_0-km1lm_1-l。所以问题转化为在 SS 中选出若干个数字,使得 AA 中数字中 00 的个数为 kk11 的个数为 ll

使用多维 01 背包算法即可解决,时间复杂度为 O(nm0m1)O(nm_0m_1),其中 nn 为数字的数量,m0m_0m1m_1 分别为 0011 的数量。

三、代码实现

  1. 最后一块石头的重量 II
public int lastStoneWeightII(int[] stones) {
    int sum = 0;
    for (int stone : stones) {
        sum += stone;
    }
    int capacity = sum / 2;
    int[] dp = new int[capacity + 1];
    for (int stone : stones) {
        for (int j = capacity; j >= stone; j--) {
            dp[j] = Math.max(dp[j], dp[j - stone] + stone);
        }
    }
    return sum - 2 * dp[capacity];
}
  1. 目标和
public int findTargetSumWays(int[] nums, int target) {
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    if (sum < target || (sum + target) % 2 != 0) {
        return 0;
    }
    int capacity = (sum + target) / 2;
    int[] dp = new int[capacity + 1];
    dp[0] = 1;
    for (int num : nums) {
        for (int j = capacity; j >= num; j--) {
            dp[j] += dp[j - num];
        }
    }
    return dp[capacity];
}
  1. 一和零
public int findMaxForm(String[] strs, int m, int n) {
    int[][] dp = new int[m + 1][n + 1];
    for (String str : strs) {
        int count0 = 0, count1 = 0;
        for (char ch : str.toCharArray()) {
            if (ch == '0') {
                count0++;
            } else {
                count1++;
            }
        }
        for (int i = m; i >= count0; i--) {
            for (int j = n; j >= count1; j--) {
                dp[i][j] = Math.max(dp[i][j], dp[i - count0][j - count1] + 1);
            }
        }
    }
    return dp[m][n];
}

四、总结

动态规划问题的解法往往可以转化为背包问题,通过定义状态和状态转移方程,使用 0-1 背包或多维 01 背包算法求解。在实际应用中,需要注意时间和空间复杂度的问题,可以通过优化算法和数据结构来提高效率。