leetCode 2915. 和为目标值的最长子序列的长度 + 动态规划 +01背包 + 空间优化 + 记忆化搜索 + 递推

文章讲述了如何使用动态规划解决LeetCode题目2915,即寻找和为目标值的最长子序列长度,介绍了递归、记忆化搜索以及不同优化版本的解决方案,重点在于从递归到递推的转换和空间优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2915. 和为目标值的最长子序列的长度 - 力扣(LeetCode)


给你一个下标从 0 开始的整数数组 nums 和一个整数 target 。返回和为 target 的 nums 子序列中,子序列 长度的最大值 。如果不存在和为 target 的子序列,返回 -1 。子序列 指的是从原数组中删除一些或者不删除任何元素后,剩余元素保持原来的顺序构成的数组。

(一)回溯 

  • f(i,j) 表示在物品集 nums 的前 i 选取物品,使得装满容量j 的背包有最多个物品
  • 也就是f(i,j) 表示在数组 nums 前 i 个的选取元素,使得这些元素之和等于 j 最长子序列长度
  • dfs(i,j) = max(dfs(i-1,j),dfs(i-1,j-nums[i])+1)
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 翻译成递推

  • dfs(i,j) = max(dfs(i-1,j),dfs(i-1,j-nums[i])+1)
  • f(i,j) = max(f(i-1,j),f(i-1,j-nums[i])+1);
  • f(i+1,j) = max(f(i,j),f(i,j-nums[i])+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复盘_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1Zg4y197BR/?spm_id_from=333.788.top_right_bar_window_history.content.click&vd_source=a934d7fc6f47698a29dac90a922ba5a3

动态规划入门:从记忆化搜索到递推_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1Xj411K7oF/?spm_id_from=333.788&vd_source=a934d7fc6f47698a29dac90a922ba5a3

2915. 和为目标值的最长子序列的长度 - 力扣(LeetCode)icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/134179583?spm=1001.2014.3001.5501

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呵呵哒( ̄▽ ̄)"

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值