我们假设数组A和B分别为这两个序列,我们定义状态dp[i][j]表示以A[i]和B[j]结尾的并且LCIS的最后一个元素小于A[i]的值,也就是说dp[i][j]并不一定是串A[1--i]和串B[1--j]的LCIS,因为多了一个限制就是该LCIS的最后一个元素必须小于A[i]。那么闲杂我们看看如何来进行状态转移呢?
如果A[i] == B[j],那么dp[i][j] = max(dp[i`][j-1]) +1;(1<i`<i)
否则dp[i][j] = dp[i][j-1]
解题思路:如何把这个问题分解成子问题呢?经过分析,发现 “求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”是个好的子问题――这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N 个子问题都解决了,那么这N 个子问题的解中,最大的那个就是整个问题的解。由上所述的子问题只和一个变量相关,就是数字的位置。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak 做为“终点”的最长上升子序列的长度。这个问题的状态一共有N 个。状态定义出来后,转移方程就不难想了。假定MaxLen (k)表示以ak 做为“终点”的最长上升子序列的长度,那么:
MaxLen (1) = 1
MaxLen (k) = Max { MaxLen (i):1<i < k 且 ai < ak 且 k≠1 } + 1
最长上升子序列(LIS)长度的O(nlogn)算法
http://www.slyar.com/blog/longest-ordered-subsequence.html
【经典问题】最长公共上升子序列
http://www.clarkok.com/2011/08/%E3%80%90%E7%BB%8F%E5%85%B8%E9%97%AE%E9%A2%98%E3%80%91%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97/
procedure work;
var
i,j,k:integer;
begin
for i:= 1 to n do
begin
k:=0;
for j:= 1 to m do
begin
if a[i] = b[j] && f[j] < f[k]+1 then //b[k] 一定满足<a[i] ,见下面改变k的条件
f[j]:=f[k]+1;
if a[i] > b[j] && f[k] <f[j] then
k:=j; // 更新新的k
end;
end;
for i:= 1 to m do
if ans < f[i] then
ans:=f[i];
end;
2 根据网上写出的O(n*m)算法,有待加强理解,仍然WA
#include <iostream>
#include<stdio.h>
#include <cstring>
#include <list>
using namespace std;
#define MAX_N (500+1)
int dp[MAX_N];//设F[j]为必选择B[j]为末尾时的最长
int pre[MAX_N][MAX_N];
int N[MAX_N],M[MAX_N];
#define printf //
int n,m;
//同时用二维数组pre[i][j]存储上个最长长度是以seq2[]的哪个结尾的,假设之前的最长长度是dp[i'][j'],则pre[i][j] = j'
int solve()
{
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
dp[0]=0;
int maxp = 0,maxi,maxj;
//dp[i][j] 表示长度为i,长度为j的字串的最长上升子序列的长度,其中上升子序列中最大元素必须是N[i],即第一个字串的第i个元素
for(int i=1;i<=n;i++)
{
int k=0;
int maxv= N[i];
for(int j=1;j<=m;j++)
{
if(M[j] == maxv&& dp[j]< dp[k] + 1)
{
dp[j] = dp[k] +1;
pre[i][j]=k;
printf("[%d][%d]=%d dp[%d]=%d\n",i,j,k,j,dp[j]);
}
if(M[j] < maxv && dp[j] > dp[k])
{
k = j; //k为 序列里最大的元素小于 N[i]的最长递增子序列对应的j的记录
}
if(dp[j] > maxp)
{
maxp = dp[j];
maxi=i;
maxj=j;
}
}
}
int _m=0;
while( true)
{
if(maxi < 0 || maxj <0)
break;
while(maxj > 0)
{
M[_m++] = M[maxj];
printf("maxi=%d,maxj=%d\n",maxi,maxj);
maxj = pre[maxi][maxj];
}
maxi--;
printf("out while,maxi=%d,maxj=%d\n",maxi,maxj);
}
cout<<maxp<<" "<<endl;
for(int j=_m-1;j>=0;j--)
cout<<M[j]<<" ";
cout<<endl;
return 0;
}
int main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++)
cin>>N[i];
cin>>m;
for(int j=1;j<=m;j++)
cin>>M[j];
solve();
}
}
1 --------------------------------------自己写出的O( n*m*n*m)算法
#include <iostream>
#include<stdio.h>
#include <cstring>
#include <list>
using namespace std;
#define MAX_N (500+1)
int dp[MAX_N][MAX_N];
int dpMax[MAX_N][MAX_N];
int N[MAX_N],M[MAX_N];
struct point{
int i;
int j;
int flag;
};
point dpPrint[MAX_N][MAX_N];
int n,m;
int get_max(int n,int m,int &maxp,int &maxii,int &maxjj,int &maxn)
{
int _maxp = 0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(dp[i][j] > _maxp)
{
_maxp = dp[i][j];
maxii=i;
maxjj=j;
maxp=_maxp;
maxn = dpMax[i][j];
}
}
}
return 0;
}
int solve()
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
dp[i][0] = 0;
}
for(int j=1;j<=m;j++)
{
dp[0][j] = 0;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(N[i] == M[j])
{
//if(i > 1)//
int maxp=0;
int maxii=0;
int maxjj=0;
for(int ii=1;ii<i;ii++)
{
for(int jj=1;jj<j;jj++)
{
if( dp[ii][jj] > maxp && N[i] > dpMax[ii][jj])
{
maxp = dp[ii][jj];
maxii=ii;
maxjj=jj;
}
}
}
dp[i][j] = maxp +1;
dpMax[i][j] = N[i];
dpPrint[i][j].i=maxii;
dpPrint[i][j].j = maxjj;
dpPrint[i][j].flag = 3;
}
else
{
int maxp1=0,maxii1,maxjj1,max1;
int maxp2=0,maxii2,maxjj2;
get_max(i,j,maxp1,maxii1,maxjj1,max1);
if(maxp1 > 0)
{
dp[i][j] = maxp1;
dpMax[i][j] = max1;
dpPrint[i][j].i=maxii1;
dpPrint[i][j].j=maxjj1;
}
}
}
}
int maxp = 0;
int maxii=0;
int maxjj=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(dp[i][j] > maxp)
{
maxp = dp[i][j];
maxii = i;
maxjj = j;
}
cout<<maxp<<" "<<endl;
m=0;
while(maxii > 0 && maxjj > 0)
{
point p = dpPrint[maxii][maxjj];
if(p.flag == 3)
M[m++] = N[maxii];
maxii = p.i;
maxjj = p.j;
}
for(int _m = m-1;_m>=0;--_m)
cout<< M[_m]<<" ";
cout<<endl;
return 0;
}
int main()
{
while(cin>>n)
{
for(int i=1;i<=n;i++)
cin>>N[i];
cin>>m;
for(int j=1;j<=m;j++)
cin>>M[j];
solve();
}
}