LCIS(最长公共上升子序列)
方案输出+dp储存最优值优化
分析:
这道题明显是将LCS和LIS结合,那么可以想到(我没想到)定义dp[ i ] [ j ]是a中1~i 和 b中1~j 以 b[ j ] 结尾的最长LCIS长度。
转移:当a[ i ]==b[ j ]时,要在小于j中找到一个最大的dp[i-1][k]并满足b[k]<a[i] || b[k]<b[j] (因为此时a[i]==b[j],要满足上升)
优化:枚举这个k会使时间复杂度到n^3级别,所以用储存最优值的方法优化
这是没有优化的原代码
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(a[i]!=b[j]) dp[i][j]=dp[i-1][j]; else{ for(int k=1;k<j;k++) if(b[k]<a[i])//a[i]==b[j] dp[i][j]=max(dp[i][j],dp[i-1][k]+1); }
优化后完整代码
//方案输出+储存最优值优化 #include<bits/stdc++.h> using namespace std; #define N 505 int n,m,a[N],b[N],dp[N][N],path[N][N]; void dfs(int now,int p) { if(now==0) return ; dfs(now-1,path[now][p]);//now是现在输出到第几个了 if(p!=path[now][p]) printf("%d ",b[p]); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&b[i]); //没有优化的dp转移 /*for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(a[i]!=b[j]) dp[i][j]=dp[i-1][j]; else{ for(int k=1;k<j;k++) if(b[k]<a[i])//a[i]==b[j] dp[i][j]=max(dp[i][j],dp[i-1][k]+1); } }*/ //优化:储存最优值:每次用一个临时变量储存一下最优值 更新的时候直接用 而不是for一遍寻找 //当更新完一个dp 又将最优值更新 适用范围:决策集合只增不减 for(int i=1;i<=n;i++){ int k=0;//k==0!! k=j-1 k的初始值一定是0!! 因为从上面未优化中可知 k的范围是:1~j-1 而 j第一次是 1 for(int j=1;j<=m;j++){ if(a[i]!=b[j]) dp[i][j]=dp[i-1][j] ,path[i][j]=j;//路径输出:每次记录一下 填的这个数在b数组中的下标 然后递归输出 else dp[i][j]=dp[i-1][k]+1 ,path[i][j]=k; //满足单调递增的条件 且j比 k优 if( b[j]<a[i] && dp[i-1][j]>dp[i-1][k] ) k=j;//当dp被更新后 将临时变量更新一下 } } int ans=0,pos; for(int i=1;i<=m;i++) if(dp[n][i]>ans) ans=dp[n][i],pos=i; printf("%d\n",ans); if(ans) dfs(n,pos); }