1049. 最后一块石头的重量 II
题目链接: 1049. 最后一块石头的重量 II - 力扣(LeetCode)
文章讲解: 代码随想录
思路:
理解为把石头分成两堆 使得两堆的差值尽可能小 求这个最小值1
理解为往背包里装物品
每个物品的重量为石头的重量 价值也为石头的价值
dp[i][j]表示从0-i块石头往容量为j的包里装 的最大价值
状态转移:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-cost[i]]+weight[i])
压缩成一维
然后dp[j]表示容量为j的背包能装的石头的最大价值
那么sum-dp【j】就是另一个背包的石头的重量
做差求最小值即为所求
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {int sum=0;for(auto x:stones){sum+=x;}vector<int>dp(3001,0);for(int i=0;i<stones.size();i++){for(int j=sum;j>=stones[i];j--){dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);}}int min=30000;for(int i=0;i<=sum;i++){int diff=abs((sum-dp[i])-dp[i]);if(diff<min) min=diff;}return min;}
};
494. 目标和
题目链接:494. 目标和 - 力扣(LeetCode)
文章讲解:代码随想录
思路:
相当于分成两堆
用正的一堆减负的一堆等于目标
left-right=target
又right=sum-left
从而
left=(sum+target)/2
定义dp【j】表示容量为j的背包有多少种装法
重点是递推公式
dp【j】+=dp【j-nums【i】】
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(auto x:nums){sum+=x;}int left=(target+sum)/2;if((target+sum)%2!=0)return 0;if(target+sum<0)return 0;vector<int>dp(1001,0);dp[0]=1;for(int i=0;i<nums.size();i++){for(int j=dp.size()-1;j>=nums[i];j--){dp[j]+=dp[j-nums[i]];}}return dp[left];}
};
总结:
一纬dp数组j的顺序不能正序遍历的原因在于递推公式
dp【j】=max(dp【j】,dp【j-nums【i】+weights【i】】)
这里涉及到更新dp【j】顺序的问题 小的j会影响大的j
d的j不会影响小的j 所以应该先更新大j
474.一和零
题目链接:474. 一和零 - 力扣(LeetCode)
文章讲解:代码随想录
思路:
事实上 这道题就是多维度的01背包
每个字符串的价值是1
有两个维度的重量m和n
dp【m-x】【n-y】+1表示装物品i dp【m】【n】表示不装物品i
定义dp【m】【n】表示不超过m个0和n个1的情况下最大子集的长度
dp【m】【n】=max(dp【m-x】【n-y】+1,dp【m】【n】)
class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>>dp(101,vector<int>(101,0));for(auto s:strs){int x=0;int y=0;for(auto c:s){if(c=='0'){x++;}else{y++;}}for(int i=dp.size()-1;i>=x;i--){for(int j=dp[0].size()-1;j>=y;j--){dp[i][j]=max(dp[i][j],dp[i-x][j-y]+1);}}}return dp[m][n];}
};