1049. 最后一块石头的重量 II
题目链接:1049. 最后一块石头的重量 II - 力扣(LeetCode)
文章链接:代码随想录 (programmercarl.com)
视频链接:这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II
这道题其实也是个背包问题,不过要稍微动一动脑筋。怎样使剩下的石头最小?我们可以把这一堆石头尽量分成重量相近的两堆,那只要这两堆相减,就得到了最小的石头了。
这样的话,这道题就与昨天的分割等和子集一个道理了。
将石头的重量和价值都用它给我们的stones数组表示。
然后通过动规五部曲分析(详见Day41)
总代码如下:
class Solution {
public:
int lastStoneWeightII(vector& stones) {
vector dp(stones.size()*50+1,0);
int sum=0;
for(int i=0;i=stones[i];j--)
{
dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return sum-2*+dp[target];
}
};
494. 目标和
题目链接:494. 目标和 - 力扣(LeetCode)
文章链接:代码随想录 (programmercarl.com)
视频链接:装满背包有多少种方法?| LeetCode:494.目标和
这道题还是很有难度的。
首先,我们需要将这一个数组中的元素分为两堆,一堆left前面加‘+’,一堆right前面加‘-’,只要right堆-left堆=target就行了。其实就有了一个方程组。
right-left=target;
right+left=sum;
解方程组可得:left=(target+sum)/2;
那么,我们就将问题转化为求能组成left堆元素总和的方法个数。
动规五部曲:
1.确认dp数组及下标含义。
dp[j]表示填满容量为j的背包有dp[j]种填法。那么我们最后就是要求dp[left]。
2.确认递推函数
填满容量背包为j的背包,有几种填法?
我们假设背包容量为5.
递推函数永远与它的上一状态有关。
假如它的上一状态已经填了1容量,那么还剩4容量,那么就是还要dp[4]。
已经填了2,那么还需要dp[3]种方法
已经填了3,那么还需要dp[2]种方法
已经填了4,那么还需要dp[1]种方法
已经填了5,那么还需要dp[0]种方法
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
dp[j] += dp[j - nums[i]]
3.dp数组初始化。
通过递归公式可知,j永远与j-nums[i]有关,dp[0]是在公式中一切递推结果的起源,背包为0填满需要几种方法,答案是一种,因为没有方法也是一种方法。
dp[0]=1;
4.确认遍历顺序
滚动数组我,们讲过对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
5.举例推导dp数组。
总代码如下:
class Solution {
public:
int findTargetSumWays(vector& nums, int target) {
int sum=0;
for(int i=0;isum) return 0;
if((target+sum)%2==1) return 0;
int left=(target+sum)/2;
vector dp(left+1,0);
dp[0]=1;
for(int i=0;i=nums[i];j--)
{
dp[j]+=dp[j-nums[i]];
}
}
return dp[left];
}
};
474.一和零
题目链接:474. 一和零 - 力扣(LeetCode)
文章链接:代码随想录 (programmercarl.com)
视频链接:装满这个背包最多用多少个物品?| LeetCode:474.一和零
这道题其实就是一个01背包问题,只不过有两个维度而已,0的个数和1的个数,所以就需要构建二维dp数组。
动规五部曲:
1.确认dp数组和下标的定义:
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
2.确认递推公式:
dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
然后我们在遍历的过程中,取dp[i][j]的最大值。
所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);(和一维的十分相似)
3.dp数组初始化。
因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
4.确认遍历顺序。
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
5.举例推导dp数组
总代码如下:
for (string str : strs) { // 遍历物品
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
Day42打卡成功,耗时3.5小时,背包好难,一知半解。