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

posted @ 2025-06-23 14:53  Mercury_City  阅读(11)  评论(0)    收藏  举报