CF607B Zuma

1 CF607B Zuma

2 题目描述

时间限制 \(2s\) | 空间限制 \(512M\)

\(Genos\) 最近在手机上安装了游戏 \(Zuma\),在 \(Zuma\) 里面有一条线,线上有 \(n\) 个宝石,第 \(i\) 个宝石的颜色是 \(c_i\)。游戏的目标是尽快的摧毁一条线上的所有宝石。在一秒钟之内,\(Genos\) 能够准确选择一个回文串宝石并将其摧毁。摧毁掉回文串后宝石重新移位,再次形成一个新的宝石串。请你求出摧毁整个宝石串的最短时间。

数据范围:\(1 ≤ n ≤ 500\)

3 题解

这道题是一道十分明显的区间 \(dp\):我们设 \(dp_{l, r}\) 表示将区间 \([l, r]\) 内的珠子全部消除需要的操作数。我们发现:如果 \(c_l = c_r\),那么我们可以用 \(dp_{l+1, r-1}\) 来直接更新 \(dp_{l, r}\) 的值。这是因为我们可以先用 \(dp_{l+1, r-1} - 1\) 的代价将 \([l+1, r-1]\) 这一区间的值消到只剩一个回文串,这样这里面的回文串与外边的 \(c_l\)\(c_r\) 一起组成一个回文串,我们使 \([l, r]\) 区间的所有珠子全部消除的操作次数为 \(dp_{l+1, r-1} - 1 + 1\) 次,也就是 \(dp_{l+1, r-1}\) 次。

剩余的情况数便是 \(\min_{i = l}^{r-1} \limits (dp_{l, i} + dp_{i+1, r})\)。也就是说,我们任意在 \([l, r]\) 中取一个断点 \(i\),计算把 \([l, i]\) 全部消除的操作数和把 \([i+1, r]\) 全部消除的代价,这个代价便是把 \([l, r]\) 全部消除的一种方法的代价,相当于先消除 \([l, i]\),再把 \([i+1, r]\) 消除。由于 \(dp_{l, i}\)\(dp_{i+1, r}\) 都已经计算过,所以这种转移一定成立。

这里我们需要注意的是:初值除了 \(\forall l \in [1, n],dp_{l, l} = 1\) 之外,还有 \(\forall l \in [1, n-1] \space \& \space c_l = c_{l+1}, dp_{l, l+1} = 1\) 以及 \(\forall l \in [1, n-1] \space \& \space c_l \ne c_{l+1}, dp_{l, l+1} = 2\)。这是因为,如果 \(r = l+1\),那么 \(dp_{l+1, r-1}\) 就相当于 \(dp_{l+1, l}\),此时 \(l + 1 > l\),该状态不合法。

4 代码(空格警告):

#include <iostream>
#include <cstring>
using namespace std;
const int N = 505;
int n;
int c[N], dp[N][N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> c[i];
    memset(dp, 0x3f, sizeof(dp));
    for (int i = 1; i <= n; i++) 
    {
        dp[i][i] = 1;
        dp[i][i+1] = 1 + (c[i] != c[i+1]);
    } 
    for (int l = 3; l <= n; l++)
    {
        for (int i = 1; i+l-1 <= n; i++)
        {
            int j = i+l-1;
            if (c[i] == c[j]) dp[i][j] = dp[i+1][j-1];
            for (int k = i; k < j; k++) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]);
        }
    }
    cout << dp[1][n];
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-01 21:19  David24  阅读(57)  评论(0编辑  收藏  举报