題目連結:http://codeforces.com/problemset/problem/463/D
題意:
給你k個1到n的排列,問你它們的LCS(最長公共子序列)是多長。
題解:
因為都是1到n的排列,即每個串中,1到n每個數字恰好出現一次。
將相同的數字之間相連,可以得到下面的樣子(n = 4, k = 3):
顯然,要求的LCS就等於圖中互不相交的最多連線個數。
將每一個數字看做一個節點。
若i到j有一條有向邊,則代表:
數字j的連線在i的連線的後面,且互不相交。
即:
若i->j,則要滿足所有的pos[k][i] <= pos[k][j]。
其中pos[k][i]表示第k個串中,數字i出現的位置。
O(N^2*K)建圖,最終得到的一定是一個有向無環圖。
LCS就等於這個圖上的最長路徑長度。
所以dfs跑一邊dp就行了。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <vector> 5 #define MAX_N 1005 6 #define MAX_K 10 7 8 using namespace std; 9 10 int n,k; 11 int dp[MAX_N]; 12 int a[MAX_K][MAX_N]; 13 int pos[MAX_K][MAX_N]; 14 vector<int> edge[MAX_N]; 15 16 void read() 17 { 18 cin>>n>>k; 19 for(int i=1;i<=k;i++) 20 { 21 for(int j=1;j<=n;j++) 22 { 23 cin>>a[i][j]; 24 pos[i][a[i][j]]=j; 25 } 26 } 27 } 28 29 bool is_valid(int x,int y) 30 { 31 for(int i=1;i<=k;i++) 32 { 33 if(pos[i][x]>=pos[i][y]) return false; 34 } 35 return true; 36 } 37 38 void build() 39 { 40 for(int i=1;i<=n;i++) 41 { 42 for(int j=1;j<=n;j++) 43 { 44 if(is_valid(i,j)) edge[i].push_back(j); 45 } 46 } 47 } 48 49 void dfs(int now) 50 { 51 dp[now]=1; 52 for(int i=0;i<edge[now].size();i++) 53 { 54 int temp=edge[now][i]; 55 if(dp[temp]==-1) dfs(temp); 56 dp[now]=max(dp[now],dp[temp]+1); 57 } 58 } 59 60 void work() 61 { 62 build(); 63 memset(dp,-1,sizeof(dp)); 64 int ans=0; 65 for(int i=1;i<=n;i++) 66 { 67 if(dp[i]==-1) dfs(i); 68 ans=max(ans,dp[i]); 69 } 70 cout<<ans<<endl; 71 } 72 73 int main() 74 { 75 read(); 76 work(); 77 }