您的位置:首页 > 编程语言 > C语言/C++

动态规划—最长公共上升子序列

2013-09-22 21:49 295 查看
http://www.cnblogs.com/nuoyan2010/archive/2012/10/17/2728289.html

 

对于做dp的人而言,规划处最优子结构是解决一切题目的第一步,二此题的最优越子结构规划一下,

DP[i][j] 为序列1前i个元素和序列2前j个元素最长公共上升子序列多长。

那么这个时候初始值初始化为0的话,碰到序列1和序列2相等的情况只要依靠相等位置前的序列来得出状态即可,更新完整个dp数组。

代码如下:

 

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 505;
int num1
,num2
,dp

;
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
scanf("%d",&m);
for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
memset(dp,0,sizeof(dp));
int answer=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(num1[i]==num2[j])
{
for(int k=0;k<i;k++)
if(num1[k]<num1[i])
for(int l=0;l<j;l++)
dp[i][j]=max(dp[i][j],dp[k][l]+1);
}
answer=max(answer,dp[i][j]);
}
}
printf("%d\n",answer);
if(t!=0)printf("\n");
}
return 0;
}


虽然问题是解决了,但是复杂度还是过高,有O(n^4)。现在我们可以想一想,外层循环是代表的是序列1

我们就可以想到先是序列1前1项和序列2最长公共上升子序列长度,

再循环一次,就是序列1前2想和序列2最长公共子序列长度。

那么既然每次循环能够推动到后状态,那么现在我们改变一下原来的代码。

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 505;
int num1
,num2
,dp
;
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
scanf("%d",&m);
for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
memset(dp,0,sizeof(dp));
int answer=0;
int k;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(num1[i]==num2[j])
{
for(dp[j]=1,k=0;k<j;k++)
if(num2[k]<num2[j]&&dp[j]<dp[k]+1)
dp[j]=dp[k]+1;
}
answer=max(answer,dp[j]);
}
}
printf("%d\n",answer);
if(t!=0)printf("\n");
}
return 0;
}


这个时候我们又可以想到,既然每次的后状态都依赖到了前状态,那么我是不是可以记录住前状态在DP[i][j]的数组当中然后,而不需要每次遇到相同的时候我们都从前状态当中去找那个形成的最长序列,答案就是用一个值来维护更新。改变代码为:

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 505;
int num1
,num2
,f

;
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&num1[i]);
scanf("%d",&m);
for(int j=1;j<=m;j++)scanf("%d",&num2[j]);
memset(f,0,sizeof(f));
int answer=0;
int ma;
for(int i=1;i<=n;i++)
{
ma=0;
for(int j=1;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(num1[i]>num2[j]&&f[i-1][j]>ma)ma=f[i-1][j];
if(num1[i]==num2[j])f[i][j]=ma+1;
}
}
for(int j=0;j<=m;j++)answer=max(answer,f
[j]);
printf("%d\n",answer);
if(t!=0)printf("\n");
}
return 0;
}


 

如果不要求路径输出 (如 Hdu 1426) 则空间复杂度O(n),若要求路径输出,  则空间复杂度O(n^2)

 

#include <cstdio>
#include <iostream>
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;

int data1[505],data2[505];
int dp[505],pre[505][505];  //dp[j]为序列2前j个元素与序列1构成的最长公共上升子序列的长度
int num[505];

int main ()
{
int n,m,i;
while (~scanf("%d",&n))
{
for (i=1;i<=n;i++)
scanf("%d",&data1[i]);
scanf("%d",&m);
for (i=1;i<=m;i++)
scanf("%d",&data2[i]);
memset(dp,0,sizeof(dp));
memset(pre,-1,sizeof(pre));
int x=0,y=0,ans=-1;
for (i=1;i<=n;i++)
{
int temp=0,last=0;
for (int j=1;j<=m;j++)
{
if (data2[j]<data1[i] && dp[j]>temp)
{
temp=dp[j];
last=j;
}
if (data1[i]==data2[j])
{
dp[j]=temp+1;
pre[i][j]=last;
}
if (dp[j]>ans)
{
ans=dp[j];
x=i;    //最终结果x不一定等于n!
y=j;
}
}
}
printf("%d\n",ans);
int cas=ans;
for (i=ans;i>=1;i--)
{
num[i]=data2[y];
y=pre[x--][y];
while (data1[x]!=data2[y])
x--;
}
for (i=1;i<=cas;i++)
printf (i==cas?"%d\n":"%d ",num[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 动态规划 LCIS