Description

给定一个长度为 $n$ 的序列 $\{a_i\}$,可以选择一个子序列将其反转,求能获得的最大不下降子序列。

$n\leq 50$。

Solution

观察一下反转的性质,实际上就相当于将这个子序列两边对调,且不会有交错和并列,与区间 DP 能很好地搭配——选择反转两个数,他们之间的区间可以接着反转。

那么考虑 $f_{i,j}$ 表示区间 $[i,j]$ 的答案,其中只考虑是否反转 $a_i,a_j$。

考虑如何由小区间得到大区间的解,回忆 LIS 的 $O(n^2)$ 做法,与值有关,那么再加两维状态:$f_{i,j,l,r}$ 表示区间 $[i,j]$,值域 $[l,r]$ 的答案。

那么就可以方便的转移了。

首先,大值域显然可以由小值域得来,即 $f_{i,j,l,r}=\max(f_{i,j,l+1,r},f_{i,j,l,r-1})$。

为表述方便,记 $[\text{bool}]=\begin{cases}1&\text{if bool = TRUE} \\ 0&\text{if bool = FALSE}\end{cases}$​​。

当不反转 $a_i$​​,$a_j$​​ 时,$f_{i,j,l,r}=\max(f_{i+1,j,l,r}+[a_i=l],f_{i,j-1,l,r}+[a_j=r])$​​。

当反转 $a_i$,$a_j$ 时,$f_{i,j,l,r}=f_{i+1,j-1,l,r}+[a_i=l]+[a_j=r]$。

把上面的方程都取个 $\text{max}$,就得到这个状态的答案了。

初始状态就把区间长度为 $1$ 的合法状态都设成 $1$ 即可。

一开始把不相等的情况也考虑进去了,后来发现是错的:不相等的情况在值域扩张的时候会被考虑到,没必要再进行讨论。(其实把细节稍微改一下也能做)

Code

#include <algorithm>
#include <cstdio>
#include <iostream>

const int maxn = 55;

int a[maxn], b[maxn];
int f[maxn][maxn][maxn][maxn];

int n;

inline void upd_max(int &a, int b) { a = std::max(a, b); }
inline int change(bool x) { return x ? 1 : 0; }

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]), b[i] = a[i];
    
    std::sort(b + 1, b + 1 + n);
    int tot = std::unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= n; i++)
        a[i] = std::lower_bound(b + 1, b + 1 + tot, a[i]) - b;
    // 其实没必要离散化。
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= a[i]; j++)
            for (int k = a[i]; k <= tot; k++)
                f[i][i][j][k] = 1;
    // DP的顺序需要考虑一下
    for (int len = 2; len <= n; len++)
    {
        for (int i = 1; i <= n; i++)
        {
            int j = i + len - 1;
            if (j > n)
                continue;
            for (int len2 = 1; len2 <= tot; len2++)
            {
                for (int l = 1; l <= tot; l++)
                {
                    int r = l + len2 - 1;
                    if (r > tot)
                        continue;
                    if (l != tot)
                        upd_max(f[i][j][l][r], f[i][j][l + 1][r]);
                    upd_max(f[i][j][l][r], f[i][j][l][r - 1]);
                    upd_max(f[i][j][l][r], f[i + 1][j][l][r] + change(a[i] == l));
                    upd_max(f[i][j][l][r], f[i][j - 1][l][r] + change(a[j] == r));
                    upd_max(f[i][j][l][r], f[i + 1][j - 1][l][r] + change(a[i] == r) + change(a[j] == l));
                }
            }
        }
    }
    printf("%d\n", f[1][n][1][tot]);
}