最后一块石头的重量
有一堆石头, 从中任选两块石头, 将他们一起粉碎, 返回此石头可能的最小重量
思路: 就是尽量让石头分成重量相同的两堆, 相撞之后剩下的石头最小, 这里的重量和价值都为stone[i]
- dp[j]数组: 表示容量为j的背包, 最多可以背dp[j]这么重的石头
- 递推公式: 和分割等和子集是一模一样的
- 初始化: 仍然是石头总重的一半
- 遍历顺序: 如果用一维dp, 那么遍历和之前都一样
根据代码发现前面全部都是一模一样的, 只不过最后的return不同, 就是总重-粉碎后的重量
class Solution {
public int lastStoneWeightII(int[] stones) {
//这里题目说清楚了, 所以我们不需要剪枝了
int sum = 0;
for(int stone : stones){
sum += stone;
}
int len = stones.length;
//仍然是定义target
//因为最后算结果的时候还会*2, 所以抵消了, 不用判断奇数
int target = sum/2;
int[] dp = new int[target + 1];
for(int i = 0; i < len; i++){
for(int j = target; j >= stones[i]; j--){
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sum - dp[target]*2;
}
}
目标和
这道题其实是有点像回溯的组合总和的, 所以一样可以用回溯法解决, 不过也会超时
他这里也是分成两个集合, 一个加法, 一个减法, 所以也用完全背包: 加法集是left, 减法集是right, left+right=sum,就是方法总和; left-right=target就是目标数, 所以可以发现 left = (target + sum)/2(这里如果除2整除不了的话, 就说明找不到, 直接return0)
- 装满容量为j的背包, 有dp[j]种方法
- 递推公式: dp[j - num[i]]推出dp[j] (比如说dp[5], 已存在1的话, 还要dp[4]推导)
- 初始化: dp[0]=1, 就是装满容量为0的背包, 就有一种方发—不装
- 遍历顺序就不废话了
细节:
- 注意这里的剪枝, 不能被整除, 则return0
- 定义size用来做容器
- 这里一定要初始化的
- 递推的for循环第二个j是从size开始-, 大于nums[i]
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int i = 0; i < nums.length; i++){
sum += nums[i];
}
//剪个枝
//之前推导的公式,不能被整除, 就说明没有组合
if((target + sum) % 2 != 0) return 0;
//定义size用来作为left容器
int size = (target + sum) / 2;
//因为现在只用正数做容器, 所以碰到负数就return
if(size < 0) return 0;
int[] dp = new int[size + 1];
//初始化数组
dp[0] = 1;
//开始递推, z这里有细节
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];
}
}
一和零
这个背包有两个维度, 一个是m, 一个是n, 而不同长度的字符串就是不同大小的待装物品
- dp[i][j], 最多有i个0和j个1的strs的最大自己的大小为dp[i][j]
- 递推公式: dp[i][j]有上一个字符串推导出来, str中有x个0, y个1: dp[i - x][j - y] + 1 (解释上一个, 就是没放x和y, 然后加上1)
- 初始化: 初始化为0就行
- 遍历顺序: 这里两个有两个维度, 两个其实都是背包, 所以都要倒序
(注意: 这里一开始要统计0和1的数量; 由于他这里是字符串, 所以为了遍历单个字符串, 还需要一个打的for循环)
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 ch : str.toCharArray()){
//计算0和1的数量
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];
}
}