一、题目描述
- 最后一块石头的重量 II
有一堆石头,每块石头的重量都是正整数。每组选出两块石头,一组是 ,一组是 ,假设 ,则重量为 的石头会留下来。现在从这堆石头中选出一些石头使得留下来的石头的总重量最小,求最小的总重量。
- 目标和
给定一个整数数组 和一个整数 ,在数组中找出能够凑成 的元素组合,其中每个元素都可以被选或者不选,问有多少种不同的组合方式。
- 一和零
给定 个 和 个 ,现在从中选出若干个数字,使得选出的数字中 的个数为 , 的个数为 ,求最多可以选出多少个数字。
二、解题思路
- 最后一块石头的重量 II
这道题可以转化为一个背包问题。假设集合 中所有石头的重量和为 ,则可以将这些石头分成两组 和 ,满足 中的石头的重量和尽可能接近 ,即 。由于 和 的石头的重量和之和为 ,因此 中石头的重量和尽可能接近 。所以问题转化为求在重量和不超过 的情况下,选出的石头的重量和最大。
使用 0-1 背包算法即可解决,时间复杂度为 ,其中 为石头的数量, 为所有石头的重量和。
- 目标和
将这个问题转化为一个背包问题。假设集合 中所有数字的和为 ,则可以将这些数字分成两组 和 ,满足 中的数字的和为 ,即 。由于 和 的数字的和之和为 ,因此 中数字的和为 。所以问题转化为在 中选出若干个数字,使得这些数字的和为 。
使用 0-1 背包算法即可解决,时间复杂度为 ,其中 为数字的数量, 为所有数字的和。
- 一和零
将这个问题转化为一个背包问题。假设集合 中所有数字的个数为 ,其中 的个数为 , 的个数为 ,则可以将这些数字分成两组 和 ,满足 中的数字中 的个数为 , 的个数为 ,即 。由于 和 的数字个数之和为 ,因此 中数字的个数为 和 。所以问题转化为在 中选出若干个数字,使得 中数字中 的个数为 , 的个数为 。
使用多维 01 背包算法即可解决,时间复杂度为 ,其中 为数字的数量, 和 分别为 和 的数量。
三、代码实现
- 最后一块石头的重量 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];
}
- 目标和
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];
}
- 一和零
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 背包算法求解。在实际应用中,需要注意时间和空间复杂度的问题,可以通过优化算法和数据结构来提高效率。