P4170 [CQOI2007] 涂色

注意到两次涂色的区间要么不相交,要么一个包含另一个。如果两个区间只有一部分相交,那我们可以把左侧区间的右端点左移,使其不相交而不改变涂色结果。

基于这个结论,我们考虑区间 dp。设 \(f_{l,r}\) 表示将区间 \([l,r]\) 涂成目标颜色的最小操作数,从 \(f_{l,r-1}\) 转移到 \(f_{l,r}\)。如果

  • \(s_l=s_r\),我们发现 \(l\) 位置一定只被涂过一次(因为它是区间端点),将这一次涂色变成涂 \([l,r]\) 并放到第一次涂,即可在不增加涂色次数的情况下达成目标,转移方程为 \(f_{l,r}=f_{l,r-1}\)
  • \(s_l\neq s_r\),说明一定可以找到一个分割点,使得没有涂色操作横跨左右两边。用 \(f_{l,k}, f_{k+1,r}\) 更新 \(f_{l,r}\) 即可。
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 100;
char s[N];
int f[N][N];

int main() {
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    memset(f, 0x3f, sizeof(f));
    for (int i = 1; i <= n; i++) f[i][i] = 1;
    for (int len = 2; len <= n; len++) {
        for (int l = 1; l + len - 1 <= n; l++) {
            int r = l + len - 1;
            if (s[l] == s[r]) {
                f[l][r] = min(f[l - 1][r], f[l][r - 1]);
                continue;
            }
            for (int k = l; k < r; k++) {
                f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
            }
        }
    }
    printf("%d\n", f[1][n]);
    return 0;
}
posted @ 2025-05-17 16:18  XYukari  阅读(25)  评论(0)    收藏  举报