动态规划—最长公共上升子序列
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数组。
代码如下:
虽然问题是解决了,但是复杂度还是过高,有O(n^4)。现在我们可以想一想,外层循环是代表的是序列1
我们就可以想到先是序列1前1项和序列2最长公共上升子序列长度,
再循环一次,就是序列1前2想和序列2最长公共子序列长度。
那么既然每次循环能够推动到后状态,那么现在我们改变一下原来的代码。
这个时候我们又可以想到,既然每次的后状态都依赖到了前状态,那么我是不是可以记录住前状态在DP[i][j]的数组当中然后,而不需要每次遇到相同的时候我们都从前状态当中去找那个形成的最长序列,答案就是用一个值来维护更新。改变代码为:
如果不要求路径输出 (如 Hdu 1426) 则空间复杂度O(n),若要求路径输出, 则空间复杂度O(n^2)
对于做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; }
相关文章推荐
- 动态规划之最大公共序列+最长上升子序列
- 动态规划 最长公共子序列 最长上升子序列 最长上升公共子序列
- 动态规划——最长公共上升子序列LCIS
- 动态规划-最长上升子序列(LIS)
- 动态规划7:最长上升子序列LIS
- 动态规划----求最长上升子序列
- 动态规划之编号动态规划:hdu 1025(dp+二分 求最长上升子序列)
- 动态规划之最长上升子序列(未完待续)
- 动态规划 最长上升子序列
- 动态规划-最长上升子序列(LIS)
- NYoj拦截导弹(动态规划-最长上升子序列变形)
- 动态规划:最长上升子序列
- 动态规划-最长上升子序列(LIS)
- 动态规划之最长上升子序列
- 动态规划4_最长上升子序列
- 算法学习之动态规划--最长上升子序列
- 算法基础之python实现动态规划中数字三角形和最长上升子序列问题
- 动态规划-最长上升子序列(LIS)
- 动态规划 最长上升子序列(LIS)
- 动态规划(最长上升子序列)