poj 2127
题目要求出最长公共上升子序列,我们知道,求最长公共子序列的算法是O(n^2)的,求最长上升子序列是O(n*(logn))的。所以这两个问题合在一起,复杂度不会低于O(n*logn)
由于数据给到500,所以O(n^3) 的算法是可以过的。
定义max[i][j]为A串匹配到i,B串匹配到j时的最长公共上升子序列的长度。
若A[i]!=B[j] 那么max[i][j]=MAX{max[i'][j']|i'<i,j'<j}
若A[i]==B[j] 那么max[i][j]=MAX{max[i'][j']+1|i'<i,j'<j,A[i']<A[i],B[j']<B[j]}
这一看就是一个O(n^4) 的算法,但是我们会发现max[i][j]长度会增加1的时候都是在A[i]==B[j]情况下,那么我们应该考虑如何应用这个性质呢。
所以我们以A串为基准,重新定义max[i][j]:以A[i]为基准匹配串末尾时(A[i]必选),匹配到B[j]时的最长公共上升子序列的长度
若A[i]!=B[j] 那么max[i][j]=max[i][j-1]
若A[i]==B[j] 那么max[i][j]=MAX{max[i'][j-1]+1|i'<i,A[i']<A[i]}
通过这样的定义我们保证了,对于任何max[i][j],max[i][j]里面存放的是已当前A[i]为匹配的串的最后一个元素时的最大长度
我们可以看到这个方程的复杂度只有O(n^3) ,对于j的循环就没有必要了。
代码:
#include<iostream>
#include<fstream>
using namespace std;
int dp[501][501];
int a[501],b[501];
int n,m;
void print(int s,int t){
if(t==0||s==0) return;
if(a[s]!=b[t])
print(s,t-1);
else
{
int minn=0;int j;
for(int k=1;k<s;k++)
if(a[k]<a[s]&&minn<dp[k][t-1])
{
minn=dp[k][t-1];
j=k;
}
if(minn!=0)
print(j,t-1);
cout<<a[s]<<' ';
}
}
void read(){
// ifstream cin("in.txt");
int i,j,k;
while(cin>>n)
{
for(i=1;i<=n;i++)
cin>>a[i];
cin>>m;
for(j=1;j<=m;j++)
cin>>b[j];
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
if(a[i]!=b[j])
dp[i][j]=dp[i][j-1];
else
{
int s=0;
for(k=1;k<i;k++)
if(a[k]<a[i]&&s<dp[k][j-1])
{
s=dp[k][j-1];
}
dp[i][j]=s+1;
}
}
int res=0;
for(i=1;i<=n;i++)
{
if(res<dp[i][m])
{
res=dp[i][m];
j=i;
}
}
cout<<res<<endl;
if(res!=0)
{
print(j,m);
}
cout<<endl;
}
}
int main(){
read();
return 0;
}
浙公网安备 33010602011771号