CDOJ1300 LCIS

这道题当初想当然的认为 LCIS 只要求出 LCS 记录路径然后求 LCS 的 LIS 就可以,发现根本不是这回事儿。。

这题很明显的 dp,但是转移方程写的各种戳。。。。- -

最裸的 dp 方程:

dp[i][j]=max{dp[ki][kj]}+1dp[i][j]=\max\{dp[k_i][k_j]\}+1

其中:

{ki<ikj<jai=akibj=bkj\begin{cases} k_i<i \\ k_j<j \\ a_i = a_{k_i} \\ b_j = b_{k_j} \end{cases}

dp[i][j]dp[i][j] 表示以 aia_ibjb_j 结尾的 LCIS。

这样转移正确性就不做证明了,很明显,但是转移费用太高了,是 O(n2)O(n^2) 的转移,这样总的时间复杂度就变成了 O(n4)O(n^4)。对于 10001000 的数据不可接受。

改进版本:

dp[i][j]=max{dp[ki][j1]}+1dp[i][j] = \max\{dp[k_i][j-1]\}+1

其中:

{ki<iai=bj\begin{cases} ki<i \\ a_i = b_j \end{cases}

这时候 dp[i][j]dp[i][j] 表示以 aia_i 结尾,bjb_j 或其之前元素结尾的 LCIS。

这样转移就可以写成 O(n)O(n) 的转移,加上 O(n2)O(n^2) 的状态,算法复杂度是 O(n3)O(n^3)

注意,这时候还有一个很显著的优化,记录一个下标 pp 使得 apbja_p \leq b_j 并且 dp[p][j1]dp[p][j-1]dp[0][j1]dp[0][j-1]dp[i1][j]dp[i-1][j] 中最大的,这样转移就是 O(1)O(1)

dp[i][j]=dp[p][j1]+1dp[i][j]=dp[p][j-1]+1

至此,我们的 O(n2)O(n^2) 的 dp 方程就已经完成,总的复杂度就是 O(n2)O(n^2),对于 10001000 的数据量正好适合

我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;

const int MAX = 1100;
int dp[MAX][MAX], a[MAX], b[MAX];

int get() {
char ch;
int flag = 0, tmp = 0;
for (ch = getchar(); ch < 48 || ch > 57; ch = getchar())
if (ch == int('-')) break;
if (ch == int('-'))
flag = 1;
else
tmp = int(ch) - 48;
for (ch = getchar(); 48 <= ch && ch <= 57; ch = getchar())
tmp = tmp * 10 + int(ch) - 48;
return (flag) ? -tmp : tmp;
}

int main() {
int t;
int lena, lenb;
t = get();
while (t--) {
lena = get();
lenb = get();
for (int i = 1; i <= lena; i++) a[i] = get();
for (int i = 1; i <= lenb; i++) b[i] = get();
for (int i = 0; i <= lena; i++) dp[i][0] = 0;
for (int j = 1; j <= lenb; j++) {
dp[0][j] = 0;
int p = 0;
for (int i = 1; i <= lena; i++) {
dp[i][j] = dp[i][j - 1];
if (a[i] == b[j] && dp[i][j] < dp[p][j - 1] + 1) {
dp[i][j] = dp[p][j - 1] + 1;
} else if (a[i] < b[j] && dp[i][j] > dp[p][j - 1]) {
p = i;
}
}
}
int ret = 0;
for (int i = 0; i <= lena; i++) {
ret = max(ret, dp[i][lenb]);
}
printf("%d\n", ret);
}
return 0;
}