zoukankan      html  css  js  c++  java
  • 【noi 2.6_2000】&【poj 2127】 最长公共子上升序列 (DP+打印路径)

    由于noi OJ上没有Special Judge,所以我是没有在这上面AC的。但是在POJ上A了。

    题意如标题。

    解法:f[i][j]表示a串前i个和b串前j个且包含b[j]的最长公共上升子序列长度

    首先,可用3重循环得到,k循环找到b串j之前的最大长度子序列的结尾字符b[k],得以更新现在f[i][j]的状态。
    然后,由于k循环的都在j之前,可发现k循环可略去,直接将满足上升的字符用临时变量存,每次j循环的都更新就好了。

    具体上,最初f[i][j]=f[i-1][j],先继承好上一个状态,a[i]不加入选出的子序列(平常DP有表示“前...”的初始值都是像这样先可由前一个状态推出的)于是a[i]=b[j]时,就由之前存的最佳答案+1得到。最后由于这次的j是为了下次的a[i]=b[xx]做准备,所以若满足b[j]<a[i]且当前答案最佳,就更新临时储存的临时变量。

    另外,打印路径有2种方法。用1维数组存是不行的,因为相同的一个i在和不同的j时,不能直接覆盖掉原来的答案。
    1.用2个数组分别记录每次a[i]=b[j]之后ans+1的i和j;

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 typedef long long LL;
     8 const int N=510;
     9 LL a[N],b[N],s[N];
    10 int f[N][N],u[N][N],v[N][N];
    11 
    12 int main()
    13 {
    14     int n,m;
    15     scanf("%d",&n);
    16     for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    17     scanf("%d",&m);
    18     for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
    19     memset(f,0,sizeof(f));
    20     memset(u,0,sizeof(u));
    21     memset(v,0,sizeof(v));
    22     for (int i=1;i<=n;i++)
    23     {
    24      int p=0,q=0;
    25      for (int j=1;j<=m;j++)
    26      {
    27        f[i][j]=f[i-1][j];
    28        u[i][j]=u[i-1][j],v[i][j]=v[i-1][j];
    29        if (a[i]==b[j]) f[i][j]=f[p][q]+1,u[i][j]=p,v[i][j]=q;
    30        else if (a[i]>b[j] && f[i][j]>f[i][q]) p=i-1,q=j;
    31      }
    32     }
    33     int t=1,cnt=0;
    34     for (int j=2;j<=m;j++)
    35       if (f[n][j]>f[n][t]) t=j;
    36     printf("%d
    ",f[n][t]);
    37     s[++cnt]=b[t];
    38     int x=n,y=t,xx,yy;
    39     while (f[x][y])
    40       s[++cnt]=b[y],xx=x,yy=y,x=u[xx][yy],y=v[xx][yy];
    41     for (int i=cnt;i>=1;i--) printf("%lld ",s[i]);
    42     return 0;
    43 }
    View Code 1

    2.只存每次加入子序列的b[j],不断一个个递减i找到与b[j]匹配的a[i],得到下一个j的pre[i][j]。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 
     7 typedef long long LL;
     8 const int N=510;
     9 LL a[N],b[N],s[N];
    10 int f[N][N],pre[N][N];
    11 
    12 int main()
    13 {
    14     int n,m;
    15     scanf("%d",&n);
    16     for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    17     scanf("%d",&m);
    18     for (int i=1;i<=m;i++) scanf("%lld",&b[i]);
    19     memset(f,0,sizeof(f));
    20     memset(pre,0,sizeof(pre));
    21     for (int i=1;i<=n;i++)
    22     {
    23      int p=0;
    24      for (int j=1;j<=m;j++)
    25      {
    26        f[i][j]=f[i-1][j];
    27        if (a[i]==b[j]) f[i][j]=f[i][p]+1,pre[i][j]=p;
    28        else if (a[i]>b[j] && f[i-1][j]>f[i-1][p]) p=j;
    29      }
    30     }
    31     int t=1,cnt=0;
    32     for (int j=2;j<=m;j++)
    33       if (f[n][j]>f[n][t]) t=j;
    34     printf("%d
    ",f[n][t]);
    35     int x=n,y=t;
    36     while (f[x][y])
    37     {
    38       while(a[x]!=b[y]&&x) x--;
    39       s[++cnt]=b[y];
    40       y=pre[x][y];
    41     }
    42     for (int i=cnt;i>=1;i--) printf("%lld ",s[i]);
    43     return 0;
    44 }
    View Code 2
  • 相关阅读:
    stm8s103 EEPROM烧程序时能否保留
    NEC芯片特别说明
    pic中断特别说明
    删除排序链表中的重复元素 II
    被围绕的区域
    计数二进制子串
    简单工厂模式
    打家劫舍 II
    打家劫舍
    相同的树
  • 原文地址:https://www.cnblogs.com/konjak/p/5958404.html
Copyright © 2011-2022 走看看