[BZOJ]2220 Zuma2

祖玛游戏,两种颜色的球,每次可以选定一个长度为$k$的区间,区间内球同色,消除这个区间。消除后左右两边合并,但不会继续消除。求最小操作次数。

 

考虑对于一个区间,最后一个消灭的肯定是最左边的一些同色球。

为什么是这样?

首先如果只有这个区间,那么最左边永远可以指定作为最后一次消除:因为消除它不会对右边产生影响。

可能想不到的是,如果先消除左端点,右边就能跟区间外的合并,这样是不是不优了?

答案是否定的:先消除左端点,把右边合并到左边区间,可以把左端点这一段划到左边区间去,那么还是相当于右边成为了新的左端点。

 

那么$f[i][j][k]$表示$i,j$区间,消完剩余$k$个与左端点同色的,需要的次数。

$g[i][j]$表示消完$i,j$区间,需要的次数。

转移方程:

$f[i][j][k] = min{f[i][x][k] + g[x+1][j]}$

当$a[i]==a[j]$时有额外转移:$f[i][j][k]=min{f[i][j-1][k-1]}$

对于$g$数组,$g[i][j] = min{f[i][j][k]+1, g[i][x]+f[x+1][j]}$

 

代码如下:

#include <bits/stdc++.h>
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
    char c; int num, f = 1;
    while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
    while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
    return f * num;
}
int n, k, f[109][109][109], g[109][109], sum1[109], sum2[209];
char c[109];
signed main()
{
    n = read(); k = read();
    scanf("%s", c + 1);
    memset(f, 0x3f, sizeof(f));
    memset(g, 0x3f, sizeof(g));
    for(int i = 1; i <= n; i++) {
        sum1[i] = sum1[i - 1] + c[i] == 'G';
        sum2[i] = sum2[i - 1] + c[i] == 'H';
    }
/*
    for(int i = 1; i <= n; i++) 
        for(int j = i; j <= n; j++)
            if(sum1[j] - sum1[i - 1] == 0 || sum2[j] - sum2[i - 1] == 0)
                g[i][j] = 1;
*/
    for(int i = 1; i <= n; i++)
        f[i][i][1] = 0;
    for(int len = 1; len <= n; len++) {
        for(int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            for(int q = 1; q <= len; q++) {
                for(int p = i; p + 1 <= i + len - 1; p++) {
                    f[i][j][q] = min(f[i][j][q], f[i][p][q] + g[p + 1][j]);
                }
                if(c[i] == c[i + len - 1] && q > 1 && j - 1 >= i)
                    f[i][j][q] = min(f[i][j][q], f[i][j - 1][q - 1]);
                if(q >= k)
                    g[i][j] = min(g[i][j], f[i][j][q] + 1);
            }
        }
        for(int i = 1, j = i + len - 1; i + len - 1 <= n; i++) 
            for(int p = i; p + 1 <= i + len - 1; p++)
                g[i][j] = min(g[i][j], g[i][p] + g[p + 1][j]);
    }
    printf("%d\n", g[1][n] == 0x3f3f3f3f ? -1 : g[1][n]);
    return 0;
}
View Code

 

posted @ 2021-02-04 23:32  _onglu  阅读(54)  评论(0编辑  收藏  举报