最长上升子序列相关问题

博客围绕最长上升子序列相关算法题展开,包括登山、合唱队列、友好城市等题目。介绍了各题思路,如登山题需从左右分别求上升序列,部分题目还给出代码。部分题目需用动态规划,导弹防御系统则需暴力搜索dfs并贪心求解。

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

kid’s gliper

思路:

因为方向有两种选择,所以从小到大和从大到小都要搜一遍。
最长上升子序列的模板中,很重要的就是f[i] = 1;
时间复杂度 O(n2)*o(k)

#include <iostream>
#include <cstring>
using namespace std;
const int N = 105;
int f[N],dp[N];
int a[N];

int main() {
    int k;
    cin>>k;
    while(k--){
        int n;
        scanf("%d",&n);
        memset(f,0,sizeof f);
        memset(a,0,sizeof a);
        memset(dp,0,sizeof dp);
        for (int i = 1; i <=n ; ++i) {
            scanf("%d",&a[i]);
        }
        f[1]  =1;
        dp[1] = 1;
        for (int i = 2; i <= n; ++i) {
            f[i] =1;
            for (int j = 1; j <i; ++j) {
                if(a[i]<a[j])
                    f[i] = max(f[i],f[j]+1);
            }
        }
        for (int i = 2; i <= n; ++i) {
            dp[i] =1;
            for (int j = 1; j <i; ++j) {
                if(a[i]>a[j])
                    dp[i] = max(dp[i],dp[j]+1);
            }
        }


        int maxx = -0x3f3f3f3f;
        for(int i = 1;i<=n;i++){
            maxx = max(maxx,f[i]);
            maxx = max(maxx,dp[i]);
        }
        cout<<maxx<<endl;
    }
    return 0;
}

登山

思路:
从左向右求一次上升f[i],从右向左求一次上升g[i],然后找到f[i]+g[i]-1最大的点即可

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3+10;
int f[N],dp[N];
int a[N];

int main() {

        int n;
        cin>>n;
//        memset(f,1,sizeof f);
        memset(a,0,sizeof a);
//        memset(dp,1,sizeof dp);
        for (int i = 1; i <=n ; ++i) {
            scanf("%d",&a[i]);
        }

        for (int i = n; i >=1; --i) {
            f[i] = 1;
            for (int j = n; j >i; --j) {
                if(a[i]>a[j])
                    f[i] = max(f[i],f[j]+1);
            }
        }
        for (int i = 1; i <= n; ++i) {
            dp[i] = 1;
            for (int j = 1; j <i; ++j) {
                if(a[i]>a[j])
                    dp[i] = max(dp[i],dp[j]+1);
            }
        }


        int maxx = -0x3f3f3f3f;
        for(int i = 1;i<=n;i++){
            maxx = max(maxx,dp[i]+f[i]-1);
//            cout<<dp[i]<<' '<<f[i]<<endl;
        }
        cout<<maxx<<endl;

    return 0;
}

合唱队列

思路:登山问题,但是求出来的是队列剩下的人。ans = n-maxx

代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3+10;
int f[N],dp[N];
int a[N];

int main() {

        int n;
        cin>>n;
//        memset(f,1,sizeof f);
        memset(a,0,sizeof a);
//        memset(dp,1,sizeof dp);
        for (int i = 1; i <=n ; ++i) {
            scanf("%d",&a[i]);
        }

        for (int i = n; i >=1; --i) {
            f[i] = 1;
            for (int j = n; j >i; --j) {
                if(a[i]>a[j])
                    f[i] = max(f[i],f[j]+1);
            }
        }
        for (int i = 1; i <= n; ++i) {
            dp[i] = 1;
            for (int j = 1; j <i; ++j) {
                if(a[i]>a[j])
                    dp[i] = max(dp[i],dp[j]+1);
            }
        }


        int maxx = -0x3f3f3f3f;
        for(int i = 1;i<=n;i++){
            maxx = max(maxx,dp[i]+f[i]-1);
//            cout<<dp[i]<<' '<<f[i]<<endl;
        }
        cout<<maxx<<endl;

    return 0;
}

友好城市

思路:

观察两岸的城市,如果按照一边作为基准来选择,那么能选择的对面的城市是一个上升子序列,所以先排序再升。

代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e3+10;
pair<int,int> a[N];
int f[N];
int main() {
    int n;
    cin>>n;
    for(int i = 1;i<=n;i++)
        scanf("%d%d",&a[i].first,&a[i].second);
    sort(a+1,a+1+n);
    for(int i = 1;i<=n;i++){
        f[i] = 1;
        for(int j = 1;j<i;j++){
            if(a[i].second>a[j].second){
                f[i] = max(f[i],f[j]+1);
            }
        }
    }
    int ans = 0;
    for(int i = 1;i<=n;i++)
        ans = max(ans,f[i]);
    cout<<ans;
    return 0;
}

最大上升子序列和

思路:f[i]表示当前数的最大上升子序列最大和,将每次f[i]+1->f[i]+a[i]即可
代码:

#include <iostream>
using namespace std;
const int N = 1e3+10;
int f[N];
int a[N];
int main() {
    int n;
    cin>>n;
    for(int i = 1;i<=n;i++){
        cin>>a[i];
        f[i] = a[i];
    }

    int maxx = 0;
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<i;j++){
            if(a[j]<a[i])
                f[i] = max(f[i],f[j]+a[i]);
        }
        maxx = max(maxx,f[i]);
    }
    
    cout<<maxx;

    return 0;
}

拦截导弹

思路:

第一问:最长不上升子序列
第二问:最长上升子序列(其实是求最多的不上升子序列组)

代码:

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3+10;
int a[N];
int f[N];
int g[N];
int main() {
    int n = 0;
    while(cin>>a[n]) n++;

    int ans = 0;
    for(int i = 0;i<n;i++){
        f[i] = 1;
        for(int j = 0;j<i;j++){
            if(a[j]>=a[i])
                f[i] = max(f[i],f[j]+1);
        }
        ans = max(ans,f[i]);
    }

    int maxx = 0;
    for(int i = 0;i<n;i++){
        g[i] = 1;
        for(int j = 0;j<n;j++){
            if(a[i]>a[j])
                g[i] = max(g[i],g[j]+1);
        }
        maxx = max(maxx,g[i]);
    }
    cout<<ans<<endl;
    cout<<maxx;




    return 0;
}

导弹防御系统

思路:

这一道题目无法简单dp完成,因为每一个最长子序列的方向都不确定,不能由两次最长子序列的代码计算出结果,所以需要直接进行暴力搜索dfs,保存每次的up和down的组数,然后进行贪心。

代码:

#include <iostream>
using namespace std;
const int N = 55;
int a[N];
int up[N],down[N];
int ans;
int n;
void dfs(int k,int su,int sd){
    if(sd+su>=ans) return ;
    if(k==n){
        ans = su+sd;
        return ;
    }
    int x = 0;
    //先算一下严格上升的分组
    while(x<su&&up[x]>=a[k]) x++;
    int t = up[x];
    up[x] = a[k];
    if(x<su){
        dfs(k+1,su,sd);
    }
    else{
        dfs(k+1,su+1,sd);
    }
    up[x] = t;

    //再计算严格下降的分组
    int i = 0;
    while(i<sd&&down[i]<=a[k]) i++;
    t = down[i];
    down[i] = a[k];
    if(i<sd){
        dfs(k+1,su,sd);
    }
    else
    {
        dfs(k+1,su,sd+1);

    }
    down[i] = t;
}
int main() {
    while(cin>>n,n){
        for(int i =0;i<n;i++){
            cin>>a[i];
        }
        ans = n;
        dfs(0,0,0);
        cout<<ans<<endl;
    }
    return 0;
}

最长公共上升子序列

思路:
f(i,j): a的前i个数字和b的前j个数字,且由bj为结尾的最大公共上升子序列
状态转移
当ai不等于bj时,f[i][j]=f[i-1][j]
当ai等于bj时,要去求解所有在b中上升的子序列的最大值
再进行优化即可

暴力代码:

#include <iostream>
using namespace std;
const int N = 3e3+10;
int n;
int a[N],b[N];
int f[N][N];
int main() {
    cin>>n;
    for(int i = 1;i<=n;i++)
        scanf("%d",&a[i]);
    for (int i = 1; i <= n; ++i)
        scanf("%d",&b[i]);

    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++){
            f[i][j] = f[i-1][j];
            if(a[i]==b[j]){
                f[i][j] = max(f[i][j],1);
                for(int k = 1;k<j;k++){
                    if(b[k]<b[j])
                        f[i][j] = max(f[i][j],f[i][k]+1);
                }
            }
        }
    }

    int res = 0;
    for(int i = 1;i<=n;i++)
        res = max(res,f[n][i]);
    cout<<res;

    return 0;
}

优化代码:

#include <iostream>
using namespace std;
const int N = 3e3+10;
int n;
int a[N],b[N];
int f[N][N];
int main() {
    cin>>n;
    for(int i = 1;i<=n;i++)
        scanf("%d",&a[i]);
    for (int i = 1; i <= n; ++i)
        scanf("%d",&b[i]);

    for(int i = 1;i<=n;i++){
        int maxx = 1;
        for(int j = 1;j<=n;j++){
            f[i][j] = f[i-1][j];
            if(a[i]==b[j])
                f[i][j] = max(f[i][j],maxx);
            if(b[j]<a[i])
                maxx = max(maxx,f[i][j]+1);
        }
    }

    int res = 0;
    for(int i = 1;i<=n;i++)
        res = max(res,f[n][i]);
    cout<<res;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值