AcWing刷題——最長公共上升子序列(動態規劃 線性DP 前綴和)

題目描述

熊大媽的奶牛在小沐沐的薰陶下開始研究信息題目。java

小沐沐先讓奶牛研究了最長上升子序列,再讓他們研究了最長公共子序列,如今又讓他們研究最長公共上升子序列了。算法

小沐沐說,對於兩個數列 A優化

Bspa

,若是它們都包含一段位置不必定連續的數,且數值是嚴格遞增的,那麼稱這一段數是兩個數列的公共上升子序列,而全部的公共上升子序列中最長的就是最長公共上升子序列了。code

奶牛半懂不懂,小沐沐要你來告訴奶牛什麼是最長公共上升子序列。xml

不過,只要告訴奶牛它的長度就能夠了。blog

數列 Aip

B 的長度均不超過 3000get

input

輸入格式

第一行包含一個整數 N

,表示數列 AB

的長度。

第二行包含 N

個整數,表示數列 A

第三行包含 N

個整數,表示數列 B

輸出格式

輸出一個整數,表示最長公共上升子序列的長度。

數據範圍

1N3000

,序列中的數字均不超過 2311

輸入樣例:

4
2 2 1 3
2 1 2 3

輸出樣例:

2

 

分析

一開始本身的思路是想把求最長公共子序列和最長上升子序列的方法結合在一塊兒實現,後面發現本身的思路不行,由於實在想不出,就去看題解了,畢竟也是學算法沒多久,哎。看了y總的思路:原來y總定義f[i][j]的狀態就跟我不同。

原來是我根本沒有往回搜索,要知道找最長上升子序列是必定要往回看的,否則不能肯定當前的就必定是最長的那個,這一點我在寫最長上升子序列時還特別注意了下,但是到了這又忘了,說到底仍是本身練的太少了。

閆式dp分析法:

本身的代碼

 1 import java.util.Scanner;
 2 public class Main {
 3     public static void main(String[] args) {
 4         Scanner input = new Scanner(System.in);
 5         int N = input.nextInt();
 6         int[] A = new int[N];
 7         int[] B = new int[N];
 8         int[][] dp = new int[N + 10][N + 10];
 9         for (int i = 0; i < N; i++) {
10             A[i] = input.nextInt();
11         }
12         for (int i = 0; i < N; i++) {
13             B[i] = input.nextInt();
14         }
15         
16         // 初始化
17         for (int i = 0; i < N; i++) {
18             dp[0][i] = 0;
19         }
20         
21         for (int i = 0; i < N; i++) {
22             dp[i][0] = 0;
23         }
24 
25         for (int i = 1; i <= N; i++) {
26             int curMax = Integer.MIN_VALUE;
27             for (int j = 1; j <= N; j++) {
28                 if (A[i - 1] == B[j - 1]) {
29                     if (A[i - 1] <= curMax) {
30                         dp[i][j] = 1;
31                         curMax = A[i - 1];
32                         continue;
33                     }
34                     dp[i][j] = dp[i - 1][j - 1] + 1;
35                     curMax = A[i - 1];
36                 } else {
37                     // 查看前面是A數列少一位以後更長,仍是B數列少一位以後更長,選較長的那個做爲此時的長度
38                     dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
39                 }
40             }
41         }
42 
43         System.out.println(dp[N][N]);
44     }
45 }

運行結果,看到結果就知道我本身的代碼正確性都談不上,仍是要多看看大佬寫代碼的思路,不過每次都要有本身的思路在裏面,否則永遠也學不會算法

AC代碼——時間複雜度爲O(n ^ 3)

沒想到這個複雜度也能過,多是數據有點弱吧,哈哈!(不過咱們仍是應該相信科學,不能相信玄學,要儘可能掌握怎麼優化dp,這纔是咱們學算法的人該有的精神)

 1 import java.util.Scanner;
 2 
 3 public class Main {
 4     private static final int N = 3010;
 5     public static void main(String[] args) {
 6         Scanner input = new Scanner(System.in);
 7         int n = input.nextInt();
 8         int[] A = new int[N];
 9         int[] B = new int[N];
10         
11         for (int i = 1; i <= n; i++) A[i] = input.nextInt();
12         for (int i = 1; i <= n; i++) B[i] = input.nextInt();
13         
14         // 定義狀態:f[i][j] 表明A[1..i]和B[1..j]兩個序列中,以b[j]結尾的最長公共上升子序列的長度
15         int[][] f = new int[N][N];
16         
17         // 轉移方程
18         for (int i = 1; i <= n; i++) {
19             for (int j = 1; j <= n; j++) {
20                 f[i][j] = f[i - 1][j];
21                 if (A[i] == B[j]) {
22                     int maxv = 1;
23                     for (int k = 1; k < j; k++) {
24                         if (A[i] > B[k]) {
25                             maxv = Math.max(maxv, f[i - 1][k] + 1);
26                         }
27                     }
28                     // 更新
29                     f[i][j] = Math.max(f[i][j], maxv);
30                 }
31             }
32         }
33         
34         int res = 0;
35         // 最終答案枚舉子序列結尾取最大值便可。
36         for (int i = 1; i <= n; i++) {
37             res = Math.max(res, f[n][i]);
38         }
39       
40         
41         System.out.println(res);
42     }
43 }

AC代碼——時間複雜度爲O(n ^ 2)

如下這個代碼一開始不是很理解,是看了AcWing官網上y總給的解釋

 1 import java.util.Scanner;
 2 
 3 public class Main {
 4     private static final int N = 3010;
 5     public static void main(String[] args) {
 6         Scanner input = new Scanner(System.in);
 7         int n = input.nextInt();
 8         int[] A = new int[N];
 9         int[] B = new int[N];
10         
11         for (int i = 1; i <= n; i++) A[i] = input.nextInt();
12         for (int i = 1; i <= n; i++) B[i] = input.nextInt();
13         
14         // 定義狀態:f[i][j] 表明A[1..i]和B[1..j]兩個序列中,以b[j]結尾的最長公共上升子序列的長度
15         int[][] f = new int[N][N];
16         
17         // 轉移方程
18         for (int i = 1; i <= n; i++) {
19             int maxv = 1;
20             for (int j = 1; j <= n; j++) {
21                 f[i][j] = f[i - 1][j];
22                 if (A[i] == B[j]) {
23                     f[i][j] = Math.max(f[i][j], maxv);
24                 }
25                 if (A[i] > B[j]) {
26                     maxv = Math.max(maxv, f[i - 1][j] + 1);
27                 }
28             }
29         }
30         
31         int res = 0;
32         // 最終答案枚舉子序列結尾取最大值便可。
33         for (int i = 1; i <= n; i++) {
34             res = Math.max(res, f[n][i]);
35         }
36       
37         
38         System.out.println(res);
39     }
40 }

 AcWing官網題目連接:https://www.acwing.com/problem/content/description/274/

相關文章
相關標籤/搜索