SpringBoot學習(一)@Configuration
阿新 • • 發佈:2021-06-27
題目描述
給出 \(1,2,\ldots,n\) 的兩個排列 \(P_1\) 和 \(P_2\),求它們的最長公共子序列。
輸入格式
第一行是一個數 \(n\)。
接下來兩行,每行為 \(n\) 個數,為自然數 \(1,2,\ldots,n\) 的一個排列。
輸出格式
一個數,即最長公共子序列的長度。
輸入輸出樣例
輸入 #1
5
3 2 1 4 5
1 2 3 4 5
輸出 #1
3
說明/提示
-
對於 \(50%\) 的資料, \(n \le 10^3\);
-
對於 \(100%\) 的資料,\(n \le 10^5\)。
50tps
50分的做法就是LCS [1] 的模板了。
比如求a陣列和b陣列的LCS:
設\(dp[i][j]\)為a的前\(i\)位和b的前\(j\)位的LCS的長度。
狀態轉移方程很好想出來,即:
當a[i] == b[j]
時,就說明a[i]和b[j]相等了,既然a[i]和b[j]相等了,那這個狀態肯定與dp[i-1][j-1]
這個情況有關係,所以這個情況狀態的狀態轉移方程是\(dp[i - 1][j - 1] + 1\)
其他情況,即無法更新公共元素,考慮繼承已經算過的:
\(dp[i][j]=max(dp[i−1][j],dp[i][j−1])\)
程式碼:
#include <bits/stdc++.h> using namespace std; #define MAXN 1005 int a[MAXN], b[MAXN]; int dp[MAXN][MAXN]; int main() { int n; scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) scanf("%d", &b[i]); for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(a[i] == b[j]) dp[i][j] = dp[i - 1][j - 1] + 1; else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } printf("%d\n", dp[n][n]); return 0; }
所以我們得加優化。
再來認真讀讀題:
給出 \(1,2,\ldots,n\) 的兩個排列 \(P_1\) 和 \(P_2\),求它們的最長公共子序列。
排列的意思就是指\(P_1\)裡面任意兩個數都不相等,利用這點,我們就可以把他轉成一個LIS。
因為LCS是按位的、有順序的,所以我們就可以直接保證選中的順序是一樣的就行了。
比如:
3 2 1 4 5
1 2 3 4 5
把\(P_1\)的數在\(P_2\)的那個位置寫出來,即:
3 2 1 4 5
那他的LIS長度就是3了。
然後LIS就可以二分優化了,就不羅嗦了。
即最長公共子序列的英文縮寫。 ↩︎