【动态规划】俄罗斯信封套娃问题,最长回文子序列

 

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法......感兴趣就关注我吧!你定不会失望。

🌈个人主页:主页链接

🌈算法专栏:专栏链接

     我会一直往里填充内容哒!

🌈LeetCode专栏:专栏链接 

    目前在刷初级算法的LeetBook 。若每日一题当中有力所能及的题目,也会当天做完发出

🌈代码仓库:Gitee链接

🌈点击关注=收获更多优质内容🌈

目录

最长回文子序列:

题解:

代码实现:

俄罗斯信封套娃问题:

 题解:​编辑

代码实现:

单调队列优化:

最长回文子序列:

题解:

老样子,我们依旧按照这个方法去分析问题

先明白这里的集合是什么? 

要求的是回文子序列,那这里的集合必然和子序列有关,分析回文的属性.

发现:回文需要从头尾来看自然而然 dp[i][j]其中i,j的含义就为以i开头,j结尾的最长回文子序列的长度

再看看看下一个状态:这题要的属性是什么?

观察题意不难得出,是Max

再来看看最难的部分,状态转移方程的表示.上文说过i,j的含义是以i开头j结尾的回文子串的含义,

所以是由短的推到长的一个过程. 

所以从末尾开始递推,

若遇到s[i]==s[j]的情况,则让dp[i][j]+=2; 

若s[i]!=s[j]的情况,则判断 在dp[i][j-1]与dp[i-1][j]下谁更大保存较大的那个值即可.

所以dp方程为dp[i][j]=dp[i+1][j-1]+2   / /  dp[i][j]=max(dp[i+1][j],dp[i][j-1])

至此这张表就填完了,那么也可以开始解题了

注意这里有个初始条件的问题:当i与j指向同一个字母时,此时的回文长度为1,所以将dp[i][i]都设置为1

循环模式为:依次确定左端点i,遍历其右端点,寻找最长回文子序列

代码实现:

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        vector<vector<int>>dp(s.size(),vector<int>(s.size()));
        //dp[i][j] 从i-j中最长的回文子序列长度
        for(int i=0;i<s.size();i++)dp[i][i]=1;
        for(int i=s.size()-2;i>=0;i--)
        {
            for(int j=i+1;j<s.size();j++)
            {
                if(s[i]==s[j])dp[i][j]=dp[i+1][j-1]+2;
                else dp[i][j]=max(dp[i][j-1],dp[i+1][j]);
            }
        }
        return dp[0][s.size()-1];

    }
};  

俄罗斯信封套娃问题:

 题解:

依旧用这种方法来分析下题目.

先分析下问题是啥? 在一个绝对大的(长宽都大于另一个子信封的长宽)信封中看看能套几个子信封?

也就是如下图所示

 所以自然而然的我们可以想到,那如果从小到大将信封排序.找出绝对小于的情况不就满足题意了?

是的!这种思想可以解决一部分问题,例如第一个测试用例中排完序如下图所示(相等的为乱序)

排完序,若第一个数相等那么第二个数如何处理呢?

这里可以回顾下我们之前所做的最长上升子序列的问题.不就可以用来处理第二个数嘛? 

 所以现在的逻辑就是这样,若第一个数不同则直接将长度加一,若相同则寻找最长上升子序列.

但观察代码中的最后两个,若将6 4 放在6 7之前,最后取最长上升子序列时,就有可能将6 4也取到

所以我们要这样处理 若第一个数相同,则第二个数进行逆序存储,变成如下这种情况 

 这样我们仅需要对第二列数据求最长上升子序列即可,因为若第一个数相同,则小的那个不会被算入最终答案中

所以回到开头的那个问题.

这里的集合表示为dp[j],以j结尾的最长上升子序列长度

           属性为Max

           状态方程为 if(s[i]>=s[i-1])dp[i]=max(dp[j]+1,dp[i])

至此这道问题就解决完了,当然还要考虑一个dp初始值的问题:这里显然就是dp[j]=1(最短的上升子序列为其本身)

代码实现:


class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        if(envelopes.empty())return 0;
        sort(envelopes.begin(),envelopes.end(),[](const auto &c1,const auto&c2){
            return c1[0]<c2[0]||(c1[0]==c2[0] &&c1[1]>c2[1]);
        });
        vector<int>dp(envelopes.size(),1);
        for(int i=1;i<envelopes.size();i++)
        {
            for(int j=0;j<i;j++)
            {
                if(envelopes[i][1]>envelopes[j][1])
                {
                    dp[i]=max(dp[j]+1,dp[i]);
                }
            }
        }
        int ans=INT_MIN;
        for(int i=0;i<dp.size();i++)
        {
            if(dp[i]>ans)ans=dp[i];
        }
        return ans;
    }
};

单调队列优化:

这里虽然做出来了,且官解也是这样的.但力扣更新完测试用例之后就超时了.

单调队列比较像贪心,uu们可以去看看我这篇关于单调队列优化的理解.这里就不过多赘述

其大意思想就是,利用二分找到比插入值小的最大的一个值,之后将插入值更新到其前面,并更新队列长度与顶元素 


class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        if(envelopes.empty())return 0;
        sort(envelopes.begin(),envelopes.end(),[](const auto &c1,const auto&c2){
            return c1[0]<c2[0]||(c1[0]==c2[0] &&c1[1]>c2[1]);
        });
        vector<int>dp(envelopes.size()+1,1);
        int len=0;
        for(int i=0;i<envelopes.size();i++)
        {
            int l=0,r=len;
            while(l<r)
            {
                int mid=l+r+1>>1;
                if(dp[mid]<envelopes[i][1])l=mid;
                else r=mid-1;
            }
            len=max(len,r+1);
            dp[r+1]=envelopes[i][1];
        }

        return len;
    }
};

🌈本篇博客的内容【俄罗斯信封套娃问题,最长回文子序列】已经结束。

🌈若对你有些许帮助,可以点赞、关注、评论支持下博主,你的支持将是我前进路上最大的动力。

🌈若以上内容有任何问题,欢迎在评论区指出。若对以上内容有任何不解,都可私信评论询问。

🌈诸君,山顶见!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ppeua

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

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

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

打赏作者

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

抵扣说明:

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

余额充值