2915. 和為目標(biāo)值的最長(zhǎng)子序列的長(zhǎng)度 - 力扣(LeetCode)
給你一個(gè)下標(biāo)從?0?開(kāi)始的整數(shù)數(shù)組?nums
?和一個(gè)整數(shù)?target
?。返回和為?target
?的?nums
?子序列中,子序列?長(zhǎng)度的最大值?。如果不存在和為?target
?的子序列,返回?-1
?。子序列?指的是從原數(shù)組中刪除一些或者不刪除任何元素后,剩余元素保持原來(lái)的順序構(gòu)成的數(shù)組。
(一)回溯?
- f(i,j) 表示在物品集 nums 的前 i 個(gè)選取物品,使得裝滿(mǎn)容量為 j 的背包有最多個(gè)物品
- 也就是f(i,j) 表示在數(shù)組 nums 的前 i 個(gè)的選取元素,使得這些元素之和等于 j 的 最長(zhǎng)子序列長(zhǎng)度
class Solution {
public:
// 遞歸搜索
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
function<int(int,int)>dfs=[&](int i,int s) -> int {
if(i<0) {
if(s==0) return 0;
else return INT_MIN;
}
return max(dfs(i-1,s),dfs(i-1,s-nums[i])+1);
};
int ans = dfs(n-1,target);
return ans<0?-1:ans;
}
};
(二) 遞歸搜索 + 保存計(jì)算結(jié)果 = 記憶化搜索
class Solution {
public:
// 記憶化遞歸搜索
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> memo(n,vector<int>(target+1,-1));
function<int(int,int)>dfs=[&](int i,int s) -> int {
if(i<0) {
if(s==0) return 0;
else return INT_MIN;
}
int& res = memo[i][s];
if(res != -1) return res;
if (s < nums[i]) return res = dfs(i-1,s);
return res = max(dfs(i-1,s),dfs(i-1,s-nums[i])+1);
};
int ans = dfs(n-1,target);
return ans<0?-1:ans;
}
};
class Solution {
public:
// 記憶化遞歸搜索
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> memo(n+1,vector<int>(target+1,-1));
function<int(int,int)>dfs=[&](int i,int s) -> int {
if(i<0) {
if(s==0) return 0;
else return INT_MIN;
}
int& res = memo[i+1][s];
if(res != -1) return res;
// 不選
int& x = memo[i][s];
if(x == -1) x=dfs(i-1,s);
if (s < nums[i]) return res=x;
// 選
int& y = memo[i][s-nums[i]];
if(y == -1) y=dfs(i-1,s-nums[i]);
return res = max(x,y+1);
};
int ans = dfs(n-1,target);
return ans<0?-1:ans;
}
};
(三)1:1 翻譯成遞推
class Solution {
public:
// 遞推
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> f(n+1,vector<int>(target+1,INT_MIN));
f[0][0]=0;
for(int i=0;i<nums.size();i++) {
for(int j=0;j<=target;j++) {
// if(j>=nums[i]) f[i+1][j] = max(f[i][j],f[i][j-nums[i]]+1);
// else f[i+1][j] = f[i][j];
f[i+1][j] = f[i][j];
if(j>=nums[i]) f[i+1][j] = max(f[i][j],f[i][j-nums[i]]+1);
}
}
int ans = f[n][target];
return ans<0?-1:ans;
}
};
進(jìn)一步優(yōu)化:
class Solution {
public:
// 遞推 + 優(yōu)化
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> f(n+1,vector<int>(target+1,INT_MIN));
int sum=0;
for(int i=0;i<nums.size();i++) {
f[i][0]=0;
sum=min(sum+nums[i],target);
for(int j=1;j<=sum;j++) {
f[i+1][j] = f[i][j];
if(j>=nums[i]) f[i+1][j] = max(f[i][j],f[i][j-nums[i]]+1);
}
}
int ans = f[n][target];
return ans<0?-1:ans;
}
};
>>空間優(yōu)化
- 1、二維數(shù)組空間優(yōu)化
class Solution {
public:
// 二維空間優(yōu)化
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> dp(2,vector<int>(target+1,INT_MIN));
int sum=0;
for(int i=0;i<n;i++) {
dp[i%2][0]=0;
sum=min(sum+nums[i],target);
for(int j=1;j<=sum;j++) {
if(j >= nums[i]) dp[(i+1)%2][j] = max(dp[i%2][j],dp[i%2][j-nums[i]]+1);
else dp[(i+1)%2][j] = dp[i%2][j];
}
}
int ans = dp[n%2][target];
return ans<0?-1:ans;
}
};
- 2.一維空間優(yōu)化
class Solution {
public:
// 一維空間優(yōu)化
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<int> dp(target+1,INT_MIN);
dp[0]=0;
int sum=0;
for(int i=0;i<n;i++) {
sum=min(sum+nums[i],target);
for(int j=sum;j>=nums[i];j--) {
dp[j] = max(dp[j],dp[j-nums[i]]+1);
}
}
int ans = dp[target];
return ans<0?-1:ans;
}
};
內(nèi)容總結(jié)來(lái)自B站這位up主(Horn_JoJo)的視頻簡(jiǎn)介:(總結(jié)得超棒!?。。?/strong>
動(dòng)態(tài)規(guī)劃之選或者不選 通過(guò)「選」或「不選」,確定子問(wèn)題與原問(wèn)題的聯(lián)系。子問(wèn)題又可以通過(guò)同樣的「選」或者「不選」來(lái)再次找到子子問(wèn)題與子問(wèn)題的聯(lián)系。這樣就可以不斷將問(wèn)題拆成子問(wèn)題。就構(gòu)成了一個(gè)搜索樹(shù)。當(dāng)拆分到一定程度時(shí),則找到了最容易解決的子問(wèn)題。這樣就可以先將子問(wèn)題解決掉,然后反過(guò)來(lái)解決較大的子問(wèn)題。這樣就可以解決原問(wèn)題了。
- 樹(shù)的根結(jié)點(diǎn)時(shí)原問(wèn)題。樹(shù)的葉子結(jié)點(diǎn)是邊界或者最小子問(wèn)題。
- 自頂向下:直接遞歸(滿(mǎn)二叉樹(shù)本,有重復(fù)子問(wèn)題), 記憶化搜索(遞歸+記錄)減少重復(fù)計(jì)算。
- 自底向上:省略「遞」的過(guò)程,直接「歸」的過(guò)程中計(jì)算。
推薦和參考文章、視頻:?
116雙周賽T3復(fù)盤(pán)_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1Zg4y197BR/?spm_id_from=333.788.top_right_bar_window_history.content.click&vd_source=a934d7fc6f47698a29dac90a922ba5a3
動(dòng)態(tài)規(guī)劃入門(mén):從記憶化搜索到遞推_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1Xj411K7oF/?spm_id_from=333.788&vd_source=a934d7fc6f47698a29dac90a922ba5a3
2915. 和為目標(biāo)值的最長(zhǎng)子序列的長(zhǎng)度 - 力扣(LeetCode)https://leetcode.cn/problems/length-of-the-longest-subsequence-that-sums-to-target/solutions/2502839/mo-ban-qia-hao-zhuang-man-xing-0-1-bei-b-0nca/我的往期文章:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-741561.html
leetCode 198.打家劫舍 動(dòng)態(tài)規(guī)劃入門(mén):從記憶化搜索到遞推-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134179583?spm=1001.2014.3001.5501文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-741561.html
到了這里,關(guān)于leetCode 2915. 和為目標(biāo)值的最長(zhǎng)子序列的長(zhǎng)度 + 動(dòng)態(tài)規(guī)劃 +01背包 + 空間優(yōu)化 + 記憶化搜索 + 遞推的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!