P5189 [COCI 2009/2010 #5] ZUMA 题解
看到这道题由于他的相邻消去的规则,可以想到区间 dp 。但是由于我想了区间 dp 只有 \(2\) 维的情况是根本转移不了的。再加一维,那么加上哪一维,我觉得这是本题的难点。
在消化了网上的一些题解后,我认为,其实其他题解关注的是 \([l,r]\) 区间中 \(a_l\) 是如何消除的。而这一点是其他题解未说明的。
那么,如果我们关注了 \([l,r]\) 区间中 \(a_l\) 是如何消除的,那么我们自然而然的会想到,我们可能在 \(a_l\) 的前面(或后面)加上几个 \(a_l\) 然后使得其消除掉。而加不同 \(a_l\) 我们的消去方式自然不同,所以如果不加一维记录在 \(a_l\) 的前面加上几个 \(a_l\) ,那么我们其实无法确定组内是用什么方式消除的,也就无法转移了。
好,我们还是用关注 \([l,r]\) 区间中 \(a_l\) 是如何消除的的这个视角来看看状态转移。
第一种:我们在 \(a_l\) 前面加了若干个 \(a_l\)
我们需要分类讨论一下,因为如果我们加了 \(k-1\) 个 \(a_l\) 使得刚好现在有 \(k\) 个 \(a_l\) ,那么相当于把 \(a_l\) 位暴力删除掉了。否则我们就是正常转移即可。
if(p == k - 1) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][r][0]);
else dp[l][r][p] = min(dp[l][r][p], dp[l][r][p + 1] + 1);
第二种:我们将 \(a_l\) 与 [l+1,r] 之间的某个 \(a_l\)(数值相等) 强行连接起来,然后把他们一起消去
那么如果 \([l+1,r]\) 之间的某个 \(x\) ,满足 \(a_l=a_x\),那么我们可以将 \([l+1,x-1]\) 先全部消去,这个时候就只剩 \(a_l\) 和 \(a_{[x,r]}\) 了,显然有转移公式。
if(a[l] == a[x]) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][x - 1][0] + dp[x][r][min(p + 1, k - 1)]);
特别的,如果这个 \(x\) 就是 \(l+1\), 因为这个时候不存在 \([l+1,x-1]\) 了,我们需要特别处理一下,这个时候的处理也非常显然,就是将 \(l\) 并入 \(l+1\) 中
if(a[l] == a[l + 1]) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][r][min(p + 1, k - 1)]);
于是这道题所有情况我们就处理结束了。
下面给出代码:
#include <bits/stdc++.h>
using namespace std;
template <typename T>inline void read(T& t){t=0; register char ch=getchar(); register int fflag=1;while(!('0'<=ch&&ch<='9')) {if(ch=='-') fflag=-1;ch=getchar();}while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;}
template <typename T,typename... Args> inline void read(T& t, Args&... args) {read(t);read(args...);}
const int N = 105, inf = 0x3f3f3f3f;
int n, k, a[N], dp[N][N][7];
int main() {
read(n, k);
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i <= n; ++i) read(a[i]);
for(int i = 1; i <= n; ++i)
for(int p = 0; p < k; ++p)
dp[i][i][p] = k - p - 1;
for(int len = 2; len <= n; ++len) {
for(int l = 1; l <= n - len + 1; ++l) {
int r = l + len - 1;
for(int p = k - 1; p >= 0; --p) {
if(p == k - 1) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][r][0]);
else dp[l][r][p] = min(dp[l][r][p], dp[l][r][p + 1] + 1);
if(a[l] == a[l + 1]) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][r][min(p + 1, k - 1)]);
for(int x = l + 2; x <= r; ++x) if(a[l] == a[x]) dp[l][r][p] = min(dp[l][r][p], dp[l + 1][x - 1][0] + dp[x][r][min(p + 1, k - 1)]);
}
}
}
cout << dp[1][n][0] << endl;
return 0;
}

浙公网安备 33010602011771号