目录
[1049. 最后一块石头的重量 II - 力扣(LeetCode)](#1049. 最后一块石头的重量 II - 力扣(LeetCode))
[494. 目标和 - 力扣(LeetCode)](#494. 目标和 - 力扣(LeetCode))
前言:
今天我们依然暴打动态规划
1049. 最后一块石头的重量 II - 力扣(LeetCode)
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
这个问题其实很好想:我们如何把石头尽可能的撞碎呢?
很简单,我们在大体上把这些石头分成重量相似的两堆,因为如果两堆石头的重量相似,那么相撞后就一定会得到最小的值。
因此从动态分类的角度来讲,我们可以把这道题理解为是 一个重量为(sum/2)的背包,我们尽可能的装背包,之后装进背包的是一堆石头,未装进背包的是一堆石头,那么这两堆石头相减,就可以得到最小的石头重量。
那么我们就又把这道题转化为了 装与不装的01背包问题
其实这道题和我们前天做的分割等和子集是基本相同的,感兴趣的朋友可以去看一下那道题
那么既然是动态规划问题,我们就继续进行动态规划五部曲:
1.确定dp数组及其含义:dp[j] 是背包含量为j的时候所背的最大价值(在这里我们重量就是价值,价值就是重量)
2.递推公式 dp[j] = max(dp[j] , dp[ j - stones[i] ]+stones[i])
里层的减是减去容量,外面的加是加上价值,只不过是因为我们的重量和价值是一样的
3.确定初始化:全部初始化为0即可
cpp
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
int sum=0;
vector<int>dp(1501,0);
for(int a : stones)
{
sum=sum+a;
}
int target = sum/2;
for(int i=0;i<stones.size();i++)
{
for(int j= target;j>=stones[i];j--)
{
dp[j] = max(dp[j] , dp[ j - stones[i] ]+stones[i]);
}
}
return sum-dp[target]-dp[target];
}
};
在这里我们要特别对末尾进行说明,为什么是 return sum-dp[target]-dp[target];
其实很好理解,因为这里的dp数组是一堆石头,而我们用sum-dp数组就得到了另外一堆石头的重量,再减一次dp数组,实际就是模拟两堆石头进行碰撞。
494. 目标和 - 力扣(LeetCode)
给你一个非负整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
这道题其实我们可以这样理解:
一共有两个集合,两个集合分别装数字,设置目标值target,询问有几种可能使得两个集合相减的数字等于target
再优化一下:
我们可以把target也作为一个数字加到这两个集合的待选序列,询问有几种可能 使得两个集合的值相等
再转化一下思维:
,而target只有一个,也就是只能永远出现在一侧,另外一侧始终没有target,那么我们始终求没有target的那一侧不就好了嘛,换句话说,不就是在给定集合里面寻找有多少个集合的值等于(sum+target)/2/
那么我们不就又把这道题转化为了背包问题嘛?只不过以前我们总是在求是否有一种方法能把背包装满,现在求的是有多少种方法把背包装满。
因此我们按照动态规划五部曲走:
1.确定dp数组的含义:dp[j] 表示填满容量为j的背包最多有dp[j]种方法,例如dp[2]是指填满容量为2的背包最多有dp[2]种方法。
2.确定递推公式:dp[j]+=dp[j-nums[i]];
cpp
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(int a : nums)
{
sum=sum+a;
}
if((target + sum)%2==1) return 0;
if(abs(target)>sum) return 0;
else
{
int a = (target + sum) / 2;
vector<int> dp(a + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++)
{
for (int j = a; j >= nums[i]; j--)
{
dp[j] += dp[j - nums[i]];
}
}
return dp[a];
}
}
};
总结:
动态规划想清楚之后直接爆杀。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!