2915. 和为目标值的最长子序列的长度 - 力扣(LeetCode)
给你一个下标从 0 开始的整数数组 nums
和一个整数 target
。返回和为 target
的 nums
子序列中,子序列 长度的最大值 。如果不存在和为 target
的子序列,返回 -1
。子序列 指的是从原数组中删除一些或者不删除任何元素后,剩余元素保持原来的顺序构成的数组。
(一)回溯
- f(i,j) 表示在物品集 nums 的前 i 个选取物品,使得装满容量为 j 的背包有最多个物品
- 也就是f(i,j) 表示在数组 nums 的前 i 个的选取元素,使得这些元素之和等于 j 的 最长子序列长度
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;}
};
(二) 递归搜索 + 保存计算结果 = 记忆化搜索
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;}
};
进一步优化:
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));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;}
};
>>空间优化
- 1、二维数组空间优化
class Solution {
public:// 二维空间优化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.一维空间优化
class Solution {
public:// 一维空间优化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;}
};
内容总结来自B站这位up主(Horn_JoJo)的视频简介:(总结得超棒!!!)
动态规划之选或者不选 通过「选」或「不选」,确定子问题与原问题的联系。子问题又可以通过同样的「选」或者「不选」来再次找到子子问题与子问题的联系。这样就可以不断将问题拆成子问题。就构成了一个搜索树。当拆分到一定程度时,则找到了最容易解决的子问题。这样就可以先将子问题解决掉,然后反过来解决较大的子问题。这样就可以解决原问题了。
- 树的根结点时原问题。树的叶子结点是边界或者最小子问题。
- 自顶向下:直接递归(满二叉树本,有重复子问题), 记忆化搜索(递归+记录)减少重复计算。
- 自底向上:省略「递」的过程,直接「归」的过程中计算。
推荐和参考文章、视频:
116双周赛T3复盘_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Zg4y197BR/?spm_id_from=333.788.top_right_bar_window_history.content.click&vd_source=a934d7fc6f47698a29dac90a922ba5a3
动态规划入门:从记忆化搜索到递推_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Xj411K7oF/?spm_id_from=333.788&vd_source=a934d7fc6f47698a29dac90a922ba5a3
2915. 和为目标值的最长子序列的长度 - 力扣(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/我的往期文章:
leetCode 198.打家劫舍 动态规划入门:从记忆化搜索到递推-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/134179583?spm=1001.2014.3001.5501