0w1

POJ 2127 Greatest Common Increasing Subsequence ( DP )

2127 -- Greatest Common Increasing Subsequence

題意:
給兩個數列 A, B,長度分別為 N, M,求最長公共遞增子序列。

資料規模:
元素大小在 int32 範圍
N, M ≤ 500
TL: 2000 ms

解法:
顯然可以定義 O( N^3 ) 的 dp,但這裡考慮 O( N^2 )。
dp[ i ][ j ] : B[ j - 1 ] 為子序列的結尾的前提下,考慮 A[ 0, i ) 和 B[ 0, j ) 的最長公共子序列時的最長長度。
dp[ i ][ j ] = dp[ i - 1 ][ j ], if A[ i - 1 ] != B[ j - 1 ]
_______________best( i, j ), otherwise
best( i, j ) 的維護方法如此:在更新 dp 表格時,以 i = 1 -> N, j = 1 -> M 的順序進行。考慮到 j 那層迴圈,當我們要更新某個滿足 A[ i - 1 ] = B[ j - 1 ] 的 dp[ i ][ j ] 時,有某個 j' < j,使得 B[ j' - 1 ] < A[ i - 1 ],顯然 dp[ i ][ j' ] 會滿足 j' < j 且 B[ j' - 1 ] < A[ i - 1 ] = B[ i - 1 ],並且一定包含最大值。

時間 / 空間複雜度:
O( N^2 )

#include <bits/stdc++.h>
using namespace std;

signed main(){
  ios::sync_with_stdio( 0 );
  int N; cin >> N;
  vector< int > A( N );
  for( int i = 0; i < N; ++i ){
    cin >> A[ i ];
  }
  int M; cin >> M;
  vector< int > B( M );
  for( int i = 0; i < M; ++i ){
    cin >> B[ i ];
  }
  vector< vector< int > > dp( N + 1, vector< int >( M + 1 ) );
  vector< vector< int > > dc( N + 1, vector< int >( M + 1 ) );
  for( int i = 1; i <= N; ++i ){
    int best = 0, id = -1;
    for( int j = 1; j <= M; ++j ){
      if( A[ i - 1 ] == B[ j - 1 ] ){
        dp[ i ][ j ] = best + 1;
        dc[ i ][ j ] = id;
      } else{
        dp[ i ][ j ] = dp[ i - 1 ][ j ];
        if( B[ j - 1 ] < A[ i - 1 ] ){
          if( best < dp[ i ][ j ] ){
            best = dp[ i ][ j ];
            id = j;
          }
        }
      }
    }
  }
  int ans = 0, iid, jid;
  for( int i = 1; i <= N; ++i ){
    for( int j = 1; j <= M; ++j ){
      if( ans < dp[ i ][ j ] ){
        ans = dp[ i ][ j ];
        iid = i;
        jid = j;
      }
    }
  }
  cout << ans << endl;
  vector< int > stk;
  for( int i = 0, x = iid, y = jid; i < ans; ++i ){
    while( dc[ x ][ y ] == 0 ) --x;
    stk.emplace_back( B[ y - 1 ] );
    y = dc[ x ][ y ];
  }
  for( int i = ( int ) stk.size() - 1; i >= 0; --i ){
    cout << stk[ i ] << " \n"[ i == 0 ];
  }
  return 0;
}