最长公共上升子序列


:

x,y,s,

si<sj(0<=i<j<|s|).S.

比较直观的做法(O(n^4))

仿dp[i][j],xi,yj.so,

if xi != yj
    dp[i][j] = 0
else
    dp[i][j] = max(dp[ii][ij]) ( 0 <= ii < i, 0 <= ij < j, dp[ii][ij] != 0 && x[ii] < x[i]) + 1

O(n4)

O(n^3)的算法

LICSLISLCS.LISLCS.

LISdp[i]xi.

LCSdp[i][j]x[0i]y[0j].

LISdp[i]x[0i]?

LISdp[i],,x[i]

.LCSdp[i][j],.

LICS,LIS,,dp[i][j],xiyj

xi,yi,,,.

dp[i][j]x[0i]y[0j]LICS,yj.

so,

if xi != yj
    dp[i][j] = dp[i-1][j]
else
    dp[i][j] = max(dp[i-1][k])(0 < k < j && y[k] < y[j]) + 1

:

x[0...m]y[0...n],y[n]z[0...zn].

x[m]!=y[n],z[0...zn]x[0...m1]y[0...n],y[n]LICS.

x[m]==y[n],z[0...zn1]x[0...m1]y[0...k],y[k]LICS(0<k<j),.

反证:
设s, s[0...sn]为x[0...m-1]和y[0...k]上的, 以x[k]为结束的一个LICS, 并且sn > zn-1.

那么,s[0...sn] 可以加上y[n], 得到长度sn+1的一个以y[n]为结束字符的最长公共上升子序列, sn+1 > zn, 与假设矛盾.

.

O(n3).

O(n^2)对O(n^3)的一个优化.

,dp[i][j]dp[k][j1](0<k<i).

ii.

i,,O(n2).

memset(dp, 0, sizeof(dp));
for (i = 1; i <= m; i++) {
    for(j = 1; j <= n; j++) {
        dp[i][j] = 0;
        if (x[i] != y[j]) {
            dp[i][j] = dp[i-1][j];
        } else {
            for (k = 1; k < j; ++k) {
                if (dp[i][j] < dp[i - 1][k] && y[k] < y[j]) {
                    dp[i][j] = dp[i - 1][k];
                }
            }
            dp[i][j] += 1;
        }

,x[i]=y[j],dp[i][j].

O(n2).

:

for (k = 1; k < j; ++k) {
    if (dp[i][j] < dp[i - 1][k] && y[k] < y[j]) {
        dp[i][j] = dp[i - 1][k];
    }
}

y[j]=x[i],

for (k = 1; k < j; ++k) {
    if (dp[i][j] < dp[i - 1][k] && y[k] < x[i]) {
        dp[i][j] = dp[i - 1][k];
    }
}

dp[i1][k](0<k<j)y[k]<x[i]

i(),j,.

mlendp[i1][k](0<k<j)y[k]<x[i]

jO(1)mlendp[i][j].

:

for (i = 1; i <= m; i++) {
    mlen = 0;
    for(j = 1; j <= n; j++) {
        dp[i][j] = dp[i-1][j];
        //更新mlen
        if (y[j] < x[i] && dp[i - 1][j] > mlen) {
                mlen = dp[i - 1][j];
        }
        //计算dp[i][j]
        if (y[j] == x[i]) {
            dp[i][j] = mlen + 1;
        }
    }
}

O(n2)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int f[5005][5005];
int a[5005],b[5005];
int yh[5005];
int pre[5005][5005];
int main(){
  int n;
  scanf("%d",&n);
  for (int i=1;i<=n;++i) scanf("%d",&a[i]);
  int m;
  scanf("%d",&m);
  for (int i=1;i<=m;++i) scanf("%d",&b[i]);
  int ans=0;
    int x=n;
    int y=m;
  for (int i=1;i<=n;++i){
      int len=0;
      int last=0;
    for (int j=1;j<=m;++j){
        f[i][j]=f[i-1][j];
            if (a[i]>b[j]&&f[i-1][j]>len) len=f[i-1][j],last=j;
        if (a[i]==b[j]) f[i][j]=len+1,pre[i][j]=last;
            if (f[i][j]>ans){
              y=j;
              ans=f[i][j];
            }
        }
    }
    printf("%d\n",ans);
    int tail=0;
    while(ans--){
    yh[++tail]=b[y];
    while(a[x]!=b[y]) x--;
    y=pre[x][y];
    x--;
  }
    for (int i=tail;i>=2;--i) printf("%d ",yh[i]);
    printf("%d\n",yh[1]);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值