最長公共上升子序列 題解
阿新 • • 發佈:2020-09-01
原題連結:LCIS
題目大意
給定了兩個長度為\(n,m\)的序列,找出他們的最長公共子序列,要求嚴格上升,只需要長度.例如兩個序列分別是1 1 2 3
和1 2 3
那麼答案是1 2 3
長度為3.
資料範圍:\(1 \leq n,m \leq 3000\)
思路
顯然從複雜度上來說這個題要求的是一個\(O(n^2)\)的解法.那麼由於這個題本身是來自兩個經典模型LCS和LIS拼接過來的,那麼比較容易的就可以想到這個題所需的幾個關鍵資訊:兩個序列分別走到哪個位置以及當前末尾的是誰.
狀態定義:\(f[i][j]\)表示當前A序列用到了\([1,i]\)而B序列用到了\([1,j]\)且以\(B[j]\)
入口:全是\(0\),方便起見把\(A[0],B[0]\)記作\(-inf\).
轉移:可以類比原來的兩個模型得到:
- 當\(A[i] \neq B[j]\)的時候,只能退而求其次,因為這兩個位置不匹配我就只能拿上一個以\(B[j]\)結尾的狀態轉移,這樣就是最大的不至於更差,也就是說這種情況下\(f[i][j] = f[i - 1][j]\)
- 當\(A[i] = B[j]\)的時候,這裡兩個末尾的值匹配起來了,那麼上一個,也就是之前的一個可以拼接過來的狀態,首先應該是用到了A裡的前\(i-1\)個字元,同時是以某個\(k\in[1,j-1],B[k]\)
出口:\(\max_{i\in[1,m]}f[n][i]\)
那麼上述的狀態計算可以以\(O(n^3)\)的暴力遞推求解.但這顯然不夠,還得要把這個複雜度降掉一維,或者至少也要降一個\(log\)
在最開始的時候,最大值應該設定成\(1\).因為開始的時候對應過去的元素應該是\(B[0]\),而他顯然是滿足\(B[0]<A[i]\)的.
程式碼
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3005;
int a[N],b[N],f[N][N];
int main()
{
int n;scanf("%d",&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 maxv = 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],maxv);
if(b[j] < a[i]) maxv = max(maxv,f[i][j] + 1);
}
}
int res = 0;
for(int i = 1;i <= n;++i) res = max(res,f[n][i]);
cout << res;
return 0;
}