Zuma HDU - 6212(区间dp)

Zuma HDU - 6212

Think about the Zuma Game. You have a row of at most 200
black(0) or white(1) balls on the table at the start. Each three consecutive balls never share the same colour. You also have infinite amount of black and white balls in your hand. On each turn, you can choose a ball in your hand and insert it into the row, including the leftmost place and the rightmost place. Then, if there is a group of three of more balls in the same colour touching, remove these balls. Keep doing this until no more balls can be removed.
Find the minimal balls you have to insert to remove all the balls on the table.
Input
The first line of input contains an integer T (1≤T≤100) which is the total number of test cases.
Each test case contains a line with a non-empty string of 0 and 1
describing the row of balls at the start.
Output
For each test case, output the case number and the minimal balls required to insert in a line.
Sample Input

4
10101
101001001
1001001001
01001101011001100

Sample Output

Case #1: 4
Case #2: 3
Case #3: 3
Case #4: 2
题意:

一共有两种颜色的祖玛游戏,每三个连在一起(或者大于三个)的球球就会被消除掉,问将这个字符串消除干净的最小插入球的个数。

分析:

问题从小区间推到大区间

因为只有两种颜色(用01表示),我们可以把同色的分成一段一段的,并记录每段内有多少个同色的,这样每个相邻的段一定都是不同色的,相隔的段一定都是同色的。假设用 a[i]i a [ i ] 记 录 第 i 段 内 有 多 少 个 小 球

dp[i][j][i,j] d p [ i ] [ j ] 表 示 消 去 区 间 [ i , j ] , 需 要 插 入 球 的 最 少 的 数 量

共有一下三种消去方式,一一进行比较然后进行转移即可

1.将区间分成两部分,各自消除自身部分,枚举分隔位置k:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]) d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ k ] + d p [ k + 1 ] [ j ] )

2.中间区间全部消去,两端的两端颜色相同,个数大于等于三消去,否则再加1

dp[i][j]=min(dp[i][j],dp[i+1][j1]+(a[i]+a[j]==2)) d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ j − 1 ] + ( a [ i ] + a [ j ] == 2 ) )

3.最后一种方式,中间一个球和两端的颜色相同,消去左右两侧后,剩下的三部分相撞消去,枚举中间个数为1且和两端颜色相同的位置k(k+=2)

dp[i][j]=min(dp[i][j],dp[i+1][k1]+dp[k+1][j1]) d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i + 1 ] [ k − 1 ] + d p [ k + 1 ] [ j − 1 ] )

此时要求 a[i]+a[j]<4 a [ i ] + a [ j ] < 4 因为如果大于等于4,一定说明两端的段个数是2(不可能是3+1,因为3不能存在),这样无论我们先删去左边的 [i+1,k1][k+1,j1] [ i + 1 , k − 1 ] 区 间 还 是 右 边 的 [ k + 1 , j − 1 ] 区间,两端的段一定能和k位置的哪一个凑成三个直接消去,这样就不是三部分相撞消去了,就不是第三种情况了。

除此之外还要注意后两种方式都必须保证[i+1,j-1]的长度是奇数,因为这样才能保证两端是相同的颜色

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 3e2+9;

char s[maxn];
int a[maxn];//记录每段的个数
int dp[maxn][maxn];

int main(){
    int T,cas = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%s",&s);
        int n = strlen(s);
        int cnt = 1;//记录段数
        a[1] = 1;
        for(int i = 1; i < n; i++){
            if(s[i] == s[i-1]) a[cnt]++;
            else a[++cnt] = 1;
        }//每段个数
        for(int len = 0; len <= cnt; len++){
            for(int i = 1; i <= cnt; i++){
                int j = i + len;
                if(j < 1 || j > cnt) continue;
                dp[i][j] = 2 * n;//相当于初始化为无穷大
                if(len == 0)
                    dp[i][j] = max(1,3-a[i]);//每个段至少插入1个,因为三个相同的话肯定消去了不能存在
                else{
                    for(int k = i; k < j; k++){
                        dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j]);//分成左右两部分消去
                    }
                    if((j-i-1) % 2 == 1){//中间有奇数个说明两端的段必定是同种颜色
                        if(a[i] + a[j] == 2){//如果两头的段内个数都是只有一个,那么中间部分消去后还得插入一个,故加一
                            dp[i][j] = min(dp[i][j],dp[i+1][j-1]+1);
                        }
                        else{//否则就不用,因为消去中间之后,两端的段一碰头就消去了
                            dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
                        }
                        if(a[i] + a[j] < 4){//第三种消去方式,中间一个段作为分隔,把除去两端的段和中间这个分隔的段的其他部分消去后,剩下这三部分碰头消去
                            for(int k = i + 2; k < j; k += 2){
                                if(a[k] == 1)
                                    dp[i][j] = min(dp[i][j],dp[i+1][k-1] + dp[k+1][j-1]);
                            }
                        }
                    }
                }
            }
        }
        printf("Case #%d: ",++cas);
        printf("%d\n",dp[1][cnt]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值