牛客网:有关最长上升子序列的线性dp

最长上升子序列的变种,即线性dp

目录

1.拦截导弹

2.合唱队形

3.信箱嵌套


1.拦截导弹

 这题通过一个数学定理将问题转化成了求最长递增子序列的问题。

首先,第一问很明显就是求最长非递减子序列,第二问要用一个定理,可以参考:

Dilworth定理是个啥东东_litble的博客-CSDN博客_dilworth定理

最后转化了问题后,求解就显得十分的简单,数学yyds,代码如下所示:

#include<bits/stdc++.h>
using namespace std;

// 求最长非递减子序列长度
int solve1(vector<int> &height){
    int len=height.size();
    int res=INT_MIN;
    vector<int> dp(len, 1);
    for(int i=0;i<len;++i){
        for(int j=0;j<i;++j){
            if(height[i] <= height[j]){
                dp[i]=max(dp[i], dp[j]+1);
            }
        }
        res=max(res, dp[i]);
    }
    return res;
}

// 求最长递增子序列长度
int solve2(vector<int> &height){
    int len=height.size();
    int res=INT_MIN;
    vector<int> dp(len, 1);
    for(int i=0;i<len;++i){
        for(int j=0;j<i;++j){
            if(height[i] > height[j]){
                dp[i]=max(dp[i], dp[j]+1);
            }
        }
        res=max(res, dp[i]);
    }
    return res;
}

int main(){
    int n;
    cin>>n;
    vector<int> height(n);
    for(int i=0;i<n;++i){
        cin>>height[i];
    }
    int res1=solve1(height);
    int res2=solve2(height);
    cout<<res1<<endl<<res2<<endl;
    return 0;
}

2.合唱队形

 

求最少需要几个同学出列,其实可以想到求最长序列长度。 

既然是先递增后递减的一个序列格式,我们考虑求取这种序列的最长长度,求取可以将序列拆开,先从左向右求取每个以当前字符结束的最长子序列长度dpl[i],再从右向左求一遍最大长度dpr[i],那么很容易想到,以i为中心的序列的长度是dpl[i]+dpr[i]-1。

至于初始化,单个数字就可以满足条件,所以初始化的时候都为1。

代码如下所示:

#include<bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin>>n;
    vector<int> height(n);
    for(int i=0;i<n;++i){
        cin>>height[i];    
    }
    vector<int> dpl(n, 1);
    for(int i=0;i<n;++i){
        for(int j=0;j<i;++j){
            if(height[i]>height[j]){
                dpl[i]=max(dpl[i], dpl[j]+1);
            }
        }
    }
    vector<int> dpr(n, 1);
    for(int i=n-1;i>=0;--i){
        for(int j=n-1;j>i;--j){
            if(height[i]>height[j]){
                dpr[i]=max(dpr[i], dpr[j]+1);
            }
        }
    }
    int res=INT_MIN;
    for(int i=0;i<n;++i){
        res=max(res, dpl[i]+dpr[i]-1);
    }
    cout<<n-res<<endl;
    return 0;
}

3.信箱嵌套

 

 这里的最长上升子序列其实就是换成了(a,b)这个二元序的最长上升子序列,我们首先利用sort进行排序,这样保证第一维是大于等于前面的,在此基础上进行两个序列对的比较。这样我们可以求出最长上升序列。

代码如下所示:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;

bool cmp(PII &a, PII &b){
    return a.first>b.first && a.second>b.second;
}

int solve1(vector<PII> &v, vector<int> &dp, int n){
    if(dp[n]!=0) return dp[n];
    if(n==0) dp[n]=1;
    else{
        for(int i=0;i<n;++i){
            int tmp=solve1(v, dp, i);
            if(cmp(v[n], v[i])){
                dp[n]=max(dp[n], tmp+1);
            }
        }
    }
    return dp[n];
}

void solve2(vector<PII> &v, vector<int> &dp){
    dp[0]=1;
    int i,j;
    int res=INT_MIN;
    for(int i=1;i<v.size();++i){
        for(int j=0;j<i;++j){
            if(cmp(v[i], v[j])){
                dp[i]=max(dp[i], dp[j]);
            }
        }
        dp[i]+=1;
        res=max(res, dp[i]);
    }
}

int main(){
    int n;
    cin>>n;
    vector<PII> v(n);
    for(int i=0;i<n;++i){
        cin>>v[i].first;
        cin>>v[i].second;
    }
    vector<int> dp(n);
    sort(v.begin(), v.end());
//     solve1(v, dp, n-1);
    solve2(v, dp);
    int res=0;
    for(int i=0;i<n;++i){
        res=max(res, dp[i]);
    }
    cout<<res<<endl;
    return 0;
}

这里一个比较坑的点是每次要先取出最大值,之后再加一,跑跑下面这个案例就知道了

11
681 578
643 780
548 873
850 187
28 476
531 578
559 995
419 130
638 297
988 125
267 52

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值