CF1120C题解

看到题第一眼,感觉是字符串,但其实是动规

设 $dp_i$ 代表处理到前 $i$ 的最小代价,转移分两种情况:

  1. 花费 $a$ 的价值处理第 $i$ 个字符,则是 $dp_{i - 1} + a$ 的代价。
  2. 花费 $b$ 的代价处理 $i$ 到 $i - k + 1$ 的字符,假定 $i$ 到 $i - k + 1$ 与 $j$ 到 $j - k + 1$ 一样 (此处可用哈希进行判断),则是 $dp_{\max(i - k, j)} + b$ 的代价。

我们考虑复杂度,要枚举 $i, j, k$ 三个循环,一共 $O(n ^ 3)$。

我们一上来都说,不用字符串,但上述方法中有哈希,那么我们就把 $k$ 这个循环给优化掉。

显然,$dp_i$ 是单调不降的数列。

所以二情况的 $k$ 也要尽可能的大,也就是说,对于每个可行的 $j$,它的最大贡献是 $1$ 到 $i$ 与 $1$ 到 $j$ 的最长公共后缀,所以我们只用维护最长公共后缀就可以把复杂度降到 $O (n ^ 2)$,

你可能会想:最长公共后缀要后缀数组,不是用到字符串了吗?

不用那么麻烦!$n$ 只有 $5\times10^3$,暴力它不香吗?

放代码时间到!

#include <cstdio>
#include <iostream>
using namespace std;

const int maxn = 5e3 + 10;
char c[maxn];
int dp[maxn], f[maxn][maxn];

int main() {
    int n, a, b; scanf ("%d%d%d", &n, &a, &b); scanf ("%s", c + 1);
    for (int i = 1; i <= n; ++i) {
        for (int j = i + 1; j <= n; ++j) {
            if (c[i] == c[j]) f[i][j] = f[i - 1][j - 1] + 1;
            else f[i][j] = 0;
        }
    }
    for (int i = 1; i <= n; ++i) {
        dp[i] = dp[i - 1] + a;
        for (int j = 1; j < i; ++j) {
            dp[i] = min (dp[i], dp[max (i - f[j][i], j)] + b);
        }
    }
    printf ("%d\n", dp[n]);
    return 0;
}

完美结束,撒花!

posted @ 2021-11-13 22:48  wangzhongyuan  阅读(9)  评论(0编辑  收藏  举报  来源