1049.最后一块石头的重量II
题目链接/文字讲解:最后一块石头的重量II
题设:有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
- 如果
x == y,那么两块石头都会被完全粉碎; - 如果
x != y,那么重量为x的石头将会完全粉碎,而重量为y的石头新重量为y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
思路:将石头分为2组,其和尽量相近即可。动规五部曲:
1.dp数组含义:dp[i][j]代表前i块石头最接近j的重量和(重量和小于等于j)。
2.递归表达式:dp[i][j]等于dp[i-1][j]和dp[i-1][j-stone[i]]+stone[i]中的最大值
3.初始化:dp[0][stone[0]到target]初始为stone[0]。
4.递归顺序:先物品再背包,从前往后。
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = Arrays.stream(stones).sum();
int target = sum / 2;
int[][] dp = new int[stones.length][target + 1];
for (int i = stones[0]; i <= target; i++) dp[0][i] = stones[0];
for (int i = 1; i < stones.length; i++) {
for (int j = 1; j <= target; j++) {
if (j >= stones[i]){
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]);
}else {
dp[i][j] = dp[i - 1][j];
}
}
}
return (sum-2*dp[stones.length-1][target]);
}
}
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = Arrays.stream(stones).sum();
int target = sum >> 1;
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 的不同 表达式 的数目。
思路:x = (target + sum) / 2,装满容量为x的背包,有几种方法。两种无解情况:target+sum为奇数,target比sum大。动规五部曲:
1.dp数组含义:dp[j]代表装满容量为j的包,有多少种方法。
2.确定递推公式-只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。
dp[j] += dp[j - nums[i]]。解决组合问题的通用公式。
3.dp数组如何初始化-从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。
4.遍历顺序:nums放在外循环,target在内循环,且内循环倒序。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = Arrays.stream(nums).sum();
if ((sum + target) % 2 == 1) return 0;
if (target > sum) return 0;
if (target < 0 && sum < -target) return 0;
int size = (sum + target) / 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 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
思路:动规五部曲:
1.dp数组含义:dp[i][j]:最多有i个0和j个1的最大子集的大小为dp[i][j]。
2.确定递推公式:
dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
3.dp数组如何初始化,全部初始为0。
4.确定遍历顺序:外物品,里背包,从后往前。
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
int oneNum, zeroNum;
for (String str : strs) {
oneNum=0;
zeroNum=0;
for (char c : str.toCharArray()) {
if (c=='1')oneNum++;
if (c=='0')zeroNum++;
}
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];
}
}