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;
}