1049. 最后一块石头的重量 II(leetcode)
https://leetcode.cn/problems/last-stone-weight-ii/description/
思路较为巧妙的dp题,关键点在于如何将问题转化为01背包,有点贪心的思想
主要是划分为两堆尽可能相等的石碓,然后判断能否凑出这个偏小的石碓(若干石头中选,能否选出这个价值)
这里根据f[i]的定义可以有两种做法,1.f[i][j]表示前i个石头中选能否凑出重量为j的选法,类型是布尔
2.f[i][j]表示前i个石头中选,选出重量<=j的选法的最大价值,这里的价值也是重量这个数值
class Solution {
public int lastStoneWeightII(int[] stones) {
// 由于要两两相撞,因此需要分成两堆尽可能相等的石碓来相互撞
// 这样就知道目标target是多少了
// 如此就转换为01背包问题了,在若干石头中选,选出重量为目标的选法的 最大重量
// 这里重量是价值和体积
// f[i][j]表示前i个石头中选能否凑出重量为j的重量,那么答案就是f[n][target]
// f[i][j] = f[i-1][j] || f[i-1][j-stones[i]]
// f[0][0] = true; // 凑出重量为0的石头显然可以
int sum = 0;
for (int s : stones)sum += s;
int target = sum / 2;
boolean[][] f = new boolean[stones.length+10][target + 10];
f[0][0]=true; // 0不是第一个石头,而是一个石头都不选
for (int i = 1; i <= stones.length; i++)
{
for (int j = 0; j <= target; j++)
{
// 这里stones下标需要偏移一位,因为f[0][0]没选石头,因此stone[i]表示第i+1块石头
if(j>=stones[i-1])
f[i][j] = f[i-1][j] || f[i-1][j-stones[i-1]];
else f[i][j]=f[i-1][j];
}
}
// 倒序给出重量最大的答案
for(int i=target;;i--)
if(f[stones.length][i])return sum-2*i;
}
}
class Solution {
public int lastStoneWeightII(int[] stones) {
// 由于要两两相撞,因此需要分成两堆尽可能相等的石碓来相互撞
// 这样就知道目标target是多少了
// 如此就转换为01背包问题了,在若干石头中选,选出重量为目标的选法的 最大重量
// 这里重量是价值和体积
int sum = 0;
for (int s : stones) {
sum += s;
}
int target = sum / 2;
//初始化,dp[i][j]为可以放0-i物品,背包容量为j的情况下背包中的最大价值
int[][] dp = new int[stones.length][target + 1];
//dp[i][0]默认初始化为0
//dp[0][j]取决于stones[0]
for (int j = stones[0]; j <= target; j++) {
dp[0][j] = stones[0];
}
for (int i = 1; i < stones.length; i++) {
for (int j = 0; j <= target; j++) {//注意是等于
if (j >= stones[i]) {
//不放:dp[i - 1][j] 放:dp[i - 1][j - stones[i]] + 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];
}
}