最後一塊石頭的重量 II
題目連結:1049. 最後一塊石頭的重量 II - 力扣(LeetCode)
思路:儘可能將石頭分成重量相近的兩堆,結果一定最小,因此問題可以轉換為分割子集。dp[i]的含義是揹包容量為i的揹包能裝下的最大重量,由於題目中最大重量是15000,所以我們申請15001的vector。
注意,結果不是target-dp[target],儘管和sum-2*dp[target]十分相似,但是結果還是差了1。
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
vector<int> dp(15001,0);
int sum=0;
for(auto i:stones)sum+=i;
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-2*dp[target];
}
};
目標和
題目連結:494. 目標和 - 力扣(LeetCode)
思路:乍一看,確實很難把他當做一個動態規劃問題。這裡有一個公式推導:本題要如何使表示式結果為target,既然為target,那麼就一定有 left組合 - right組合 = target。left + right = sum,而sum是固定的。right = sum - left。公式來了, left - (sum - left) = target 推匯出 left = (target + sum)/2 。target是固定的,sum是固定的,left就可以求出來。
此時問題就是在集合nums中找出和為left的組合。而且要注意,(target + sum)/2向下取整也是有影響的,比如target=2,sum=5,這樣是無解的。同樣如果target>sum,也是無解的。
本題特殊之處在於之前的揹包問題都是求揹包最多裝多少東西,本題求裝滿有幾種裝法,是組合問題,我實在是想不出如何dp,投降....
dp的產生:有哪些來源可以推出dp[j]呢?只要搞到nums[i],湊成dp[j]就有dp[j - nums[i]] 種方法。
例如:dp[j],j 為5,
- 已經有一個1(nums[i]) 的話,有 dp[4]種方法 湊成 容量為5的揹包。
- 已經有一個2(nums[i]) 的話,有 dp[3]種方法 湊成 容量為5的揹包。
- 已經有一個3(nums[i]) 的話,有 dp[2]中方法 湊成 容量為5的揹包
- 已經有一個4(nums[i]) 的話,有 dp[1]中方法 湊成 容量為5的揹包
- 已經有一個5 (nums[i])的話,有 dp[0]中方法 湊成 容量為5的揹包
那麼湊整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起來。同時也要關注dp[0]的初始化問題。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(auto i:nums)sum+=i;
if(sum<abs(target))return 0;
if((sum+target)&1)return 0;
int size=(sum+target)/2;
vector<int> dp(size+1,0);
dp[0]=1;
for(int i=0;i<nums.size();i++){
for(int j=size;j>=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
return dp[size];
}
};
dp[j]+=dp[j-nums[i]];
這個公式很重要,請記住。
一和零
題目連結:474. 一和零 - 力扣(LeetCode)
思路:儘管這裡有兩個緯度的“重量”,但是該問題依然是一個01揹包問題,因為每一個物品只能取一次。
這裡我們定義一個二維dp陣列,dp[i][j]表示i個0,j個1最多能有多大的子集。先用count陣列統計每個子字串裡有多少個0和1,然後用一個三重for迴圈計算dp。
注意,二維陣列是vector<vector<int>>不是vector<int,vector<int>>,哎老糊塗了。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
vector<vector<int>> count(strs.size(),vector<int>(2,0));
for(int i=0;i<strs.size();i++){
for(char a:strs[i]){
if(a =='0') count[i][0]+=1;
else count[i][1]+=1;
}
}
for(int i=0;i<strs.size();i++){
for(int j=m;j>=count[i][0];j--){
for(int k=n;k>=count[i][1];k--){
dp[j][k]=max(dp[j][k],dp[j-count[i][0]][k-count[i][1]]+1);
}
}
}
return dp[m][n];
}
};