题意:
求最长公共上升子序列
题解:
最长公共上升子序列 = 最长公共子序列(LCS)与最长上升子序列(LIS)
LCS核心代码:
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i]==b[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
LIS核心代码:
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[j]<a[i])dp[i]=max(dp[i],dp[j]+1);
}
mx=max(mx,dp[i]);
}
最长公共上升子序列的代码就是在最长公共子序列上找一遍最长上升子序列即可。也就是在判断a[i] = = b[j]的前提下,再求出上升序列
f[i][j]代表所有a[1 ~ i]和b[1 ~ j]中以b[j]结尾的公共上升子序列的集合;
f[i][j]的值等于该集合的子序列中长度的最大值;
复杂度O(n3)
代码:
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])
{
int maxv = 1;
for (int k = 1; k < j; k ++ )
if (a[i] > b[k])
maxv = max(maxv, f[i - 1][k] + 1);
f[i][j] = max(f[i][j], maxv);
}
}
}
我们仔细看看代码,第三层for循环是用来求之前的,小于a[i]的最长公共上升子序列+1长度,实际上我们需要知道的只有之前的最长公共上升子序列的长度,这样一来我们可以用一个变量val来存放 F[ i-1 ][ k ] 中的最大值,从而就可以省略第三层循环
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 (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
}
}
我们还可以将维度缩到一维
可知f[i][j]都是由f[i-1][j]得来的
我们用f[i]表示a序列前i个元素与b序列的(LCIS)长度,t为最长LCIS的结尾元素位置,新的转移方程:
f[i] = f[t] +1 (a[i] = b[j])
这样就降到一维
for(int i=1;i<=n;i++){
maxx=0;
for(int j=1;j<=n;j++){
if(b[j]<a[i]&&maxx<f[j]) maxx=f[j];
if(b[j]==a[i]) f[j]=maxx+1;
}
}
代码:
二维空间的代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3010;
int n;
int a[N], b[N];
int f[N][N];
int main()
{
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 (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
}
}
int res = 0;
for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
printf("%d\n", res);
return 0;
}
一维数组的代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<string>
#define ll long long
#define maxn 3050
#define inf 2147483647
#define mod 10003
#define eps 1e-6
#define pi acos(-1.0)
#define de(x) ((x)*(x))
using namespace std;
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {
if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {
x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n,a[maxn],b[maxn],f[maxn],maxx;
signed main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
for(int i=1;i<=n;i++){
maxx=0;
for(int j=1;j<=n;j++){
if(b[j]<a[i]&&maxx<f[j]) maxx=f[j];
if(b[j]==a[i]) f[j]=maxx+1;
}
}
maxx=0;
for(int i=1;i<=n;i++) if(maxx<f[i]) maxx=f[i];
printf("%d",maxx);
return 0;
}