📜  门|门CS 2011 |问题 1(1)

📅  最后修改于: 2023-12-03 15:42:22.686000             🧑  作者: Mango

门|门CS 2011 |问题 1

这是门|门CS 2011年的问题1,是一道典型的动态规划问题。在这个问题中,我们需要在一个给定的序列中找到一个最长的子序列,这个子序列需要满足一些特定的条件。

问题描述

给定两个长度分别为 $n,m$ 的序列 $A,B$,我们需要在 $A$ 中找到一个最长的子序列 $L$,满足以下条件:

  1. $L$ 中的元素必须在 $B$ 中出现过。
  2. $L$ 中的元素在 $A$ 中的相对顺序与它们在 $B$ 中出现的相对顺序相同。

例如,如果 $A={1,2,3}$ 和 $B={2,1,3}$,则 $L={1,3}$ 是一个合法的解,而 $L={2,3}$ 则不合法。

解题思路

这是一道比较经典的动态规划问题。我们可以考虑把问题分成若干子问题,然后通过求解子问题来得到原问题的解。具体来说,我们可以定义一个二维数组 $dp_{i,j}$,其中 $dp_{i,j}$ 表示以 $A_i$ 和 $B_j$ 为结尾的最长子序列的长度。则我们要求的最长子序列的长度就是 $\max_{i,j} dp_{i,j}$。

开始时,所有的 $dp_{i,j}$ 均设置为0。然后,对于 $A_i$ 和 $B_j$,我们可以考虑以下两种情况:

  1. 如果 $A_i=B_j$,则 $dp_{i,j}=1+\max_{k<l,\ A_k<B_j}dp_{k,l}$,即可以将 $A_i$ 加入到以 $A_{k+1},A_{k+2},\dots,A_i$ 结尾的子序列中,其中 $k<l$,且 $A_k<B_j$。
  2. 如果 $A_i\neq B_j$,则 $dp_{i,j}=dp_{i,j-1}$,即可以忽略 $B_j$,在 $B_{j-1}$ 中寻找 $A_i$ 是否出现过。

而对于第一种情况,我们可以使用一个数组 $last_j$,其中 $last_j$ 表示 $B_j$ 在 $A$ 中最后一次出现的位置。这样,在求解 dp 值时,我们只需要枚举比 $B_j$ 小的 $A_k$ 的最大位置即可。

代码实现

下面是这个问题的 C++ 代码实现:

int dp[MAX_N][MAX_M], last[MAX_M];
memset(dp, 0, sizeof(dp));
memset(last, -1, sizeof(last));
for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
        dp[i][j] = dp[i][j - 1];
        if (a[i] == b[j]) {
            int k = last[j];
            if (k != -1 && dp[k][j - 1] + 1 > dp[i][j]) {
                dp[i][j] = dp[k][j - 1] + 1;
            }
        }
        if (a[i] == b[j]) {
            last[j] = i;
        }
    }
}

其中,$MAX_N$ 和 $MAX_M$ 分别表示序列的最大长度,$a$ 和 $b$ 分别表示两个序列。