LCIS(最长公共上升子序列)

传送门
题意:给你两串数列,让你求它俩的最长公共上升子序列
思路:令f[i][j]表示以a[1-i], b[1-j]构成的以b[j]为结尾的最长上升子序列,不难推出状态转移方程
if(a[i] != b[j]) f[i][j] = f[i-1][j]
if(a[i] == b[j]) f[i][j] = max(dp[i][j], dp[i-1][k]), 其中k满足0<k<j,a[i]>b[k]中最大的dp[i-1][k]。
状态方程是写出来了,但实现上还有一些细节需要注意。先附上代码

#include<iostream>
#include<cstdio>
using namespace std;

int dp[3001][3001]; //dp[i][j]代表a[1~i]和b[1~j]的LCIS
int a[3001], b[3001];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    for(int j = 1; j <= n; ++j)
        cin >> b[j];
    int maxx = 0;
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= n; ++j){
            if(a[i] != b[j]){
                dp[i][j] = dp[i-1][j]; //注意是不能是dp[i][j-1]
            }
            else{
                for(int k = 0; k < j; ++k){ //注意k要从0开始
                    if(a[i] > b[k])
                        dp[i][j] = max(dp[i][j], dp[i-1][k]+1);
                }
            }
            if(maxx < dp[i][j])
                maxx = dp[i][j];
        }
    }
    cout << maxx << endl;
    return 0;
}

首先,为什么是f[i][j] = f[i-1][j]而不是f[i][j] = f[i][j-1],因为根据状态的定义,必须要以b[j]结尾才行。而且可以看出,对于相同的i,k的集合是在不断扩大的,因此我们只需在j的循环里面不断更新k即可。可以变成二维复杂度

#include<iostream>
#include<cstdio>
using namespace std;

int dp[3001][3001]; //dp[i][j]代表a[1~i]和b[1~j]的LCIS
int a[3001], b[3001];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    for(int j = 1; j <= n; ++j)
        cin >> b[j];
    int maxx = 0;
    for(int i = 1; i <= n; ++i){
        int val = 0;
        for(int j = 1; j <= n; ++j){
            if(a[i] != b[j]){
                dp[i][j] = dp[i-1][j]; //注意不可以是dp[i][j-1]
            }
            else{
                dp[i][j] = max(dp[i][j], val+1);
            }
            if(b[j] < a[i])
                val = max(val, dp[i-1][j]);
            //cout << i << " " << j << " " << dp[i][j] << endl;
            if(maxx < dp[i][j])
                maxx = dp[i][j];
        }
    }
    cout << maxx << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值