HDU 1423 Greatest Common Increasing Subsequence(最長公用上升LCIS),hdulcis
HDU 1423 Greatest Common Increasing Subsequence(最長公用上升LCIS)
http://acm.hdu.edu.cn/showproblem.php?pid=1423
題意:
給你兩個數字組成的串a和b,要你求出它們的最長公用嚴格遞增子序列的長度(LCIS).
分析:
首先我們令f[i][j]==x表示是a串的前i個字元與b串的前j個字元構成的且以b[j]結尾的LCIS長度.
當a[i]!=b[j]時:
f[i][j]=f[i-1][j]
當a[i]==b[j]時:
f[i][j]=max(f[i-1][k])+1. 其中 k<j且b[k]<b[j].
如果我們按上述遞推公式依次枚舉i, j, k 的話,那麼時間複雜度就是O(n*m^2)了.
其實我們只要枚舉i, j. 然後我們記錄當前的最大f[i-1][k]值即可(要滿足k<j且b[k]<b[j]). 程式實現用到了一個技巧, 即枚舉(i, j)情況時假設a[i]的值與b[j+1]的值是相等的. 那麼只要b[j]<a[i]的話, 我們直接更新max=f[i-1][j]即可. 如果下一輪a[i]==b[j+1], 那麼上一輪max儲存的值f[i-1][j] 可以肯定j<j+1 且b[j]<a[i]==b[j+1]. (當b[j]變成b[k]也是一樣)
如何輸出一個LCIS串呢?
我們首先找到使得f[n][id]取最大值的id. 然後它肯定是由f[n-1][k](k<id且b[k]<b[id]) 構成的. 所以我們只需要往前找到那個f[n-1][k]==f[n][id]-1 且 b[k]<b[id]的值逆序輸出即可. 其實動態規劃的所有輸出方案的問題都可以這麼輸出.
如果想輸出字典序最小的LCIS串呢?
我們只需要將原來的兩個序列逆轉,然後找出最長公用遞減子序列. 然後從第一個LCDS的字元開始找儘可能字典序小的字元即可. 其實思想大致都是一樣的.
AC代碼:
</pre><pre name="code" class="cpp">#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=500+5;int n;//a串長int m;//b串長int a[maxn];//a串int b[maxn];//b串int f[maxn][maxn];int main(){ int T; scanf("%d",&T); while(T--) { 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]); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) { int max=0;//當前f[i-1][k]最大值且 k<j&&b[k]<b[j] int flag=-1; for(int j=1;j<=m;j++) { f[i][j]=f[i-1][j]; if(a[i]>b[j] && max<f[i-1][j]) { max=f[i-1][j]; flag=j; } if(a[i]==b[j]) { f[i][j]=max+1; } } } int max_val=0; int id=-1; for(int i=1;i<=m;i++) { if(max_val<f[n][i]) { max_val=f[n][i]; id=i; } } printf("%d\n",max_val); if(T) printf("\n"); //逆序輸出一個LCIS串 /* int i=n; while(id!=-1 && f[i][id]>=1) { printf("%d ",b[id]); int tmp=f[i][id]; int tmp_v=b[id]; //往前找到合法的f[i-1][k] while(id!=0) { id--; if(f[i-1][id]==tmp-1 && b[id]<tmp_v) break; } i--; } printf("\n"); */ } return 0;}