题解【Codeforces607B】Zuma

题面

题意简述:

(n) 个石头排成一排,第 (i) 个石头的颜色是 (a_i)

你可以选择一段连续的回文的子段,然后把它消去,剩下的石头会向中间靠近,把之前消去的石头的空隙补上。

问按照这样的规则,最少要几次把 (n) 个石头都消去。

( exttt{Data Range:} 1leq nleq500, 1leq a_ileq n。)

看到数据范围这么小,一眼想到区间 DP。

我们设 (dp_{i,j}) 表示消除区间 ([i, j]) 所要花费的最少次数。

一些简单的边界不难想到:

[egin{cases} dp_{i,i}=1&\ dp_{i,i+1}=1&(c_i = c_{i+1})\ dp_{i,i+1}=2&(c_i ot = c_{i+1}) end{cases}]

然后具体想一想怎么转移:

  • (c_i = c_{j}),那么很明显区间 ([i, j]) 可以和区间 ([i+1,j-1]) 一起消除,所以 (dp_{i,j}=dp_{i+1,j-1})
  • (c_i ot = c_j),那么枚举分割点 (k),消除区间 ([i,j]) 就可以转化为消除区间 ([i,k])([k+1,j]),所以 (dp_{i,j}=minlimits_{k=i}^{j-1}{dp_{i,k}+dp_{k+1,j}})

代码就很好写了。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;

inline int gi()
{
	int f = 1, x = 0; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return f * x;
}

const int INF = 0x3f3f3f3f, N = 503;

int n, m;
int a[N];
int dp[N][N];

int main()
{
    n = gi();
    for (int i = 1; i <= n; i+=1) a[i] = gi();
    memset(dp, 0x3f, sizeof dp);
    for (int i = 1; i <= n; i+=1)
        dp[i][i] = 1;
    for (int i = 1; i < n; i+=1)
        if (a[i] == a[i + 1]) dp[i][i + 1] = 1;
        else dp[i][i + 1] = 2;
    for (int len = 3; len <= n; len+=1)
        for (int i = 1; i + len - 1 <= n; i+=1)
        {
            int j = i + len - 1;
            if (a[i] == a[j]) dp[i][j] = dp[i + 1][j - 1];
            for (int k = i; k < j; k+=1)
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
        }
    printf("%d
", dp[1][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/xsl19/p/12656713.html