【区间dp】HDU6212 Zuma 经典题

来源:互联网 发布:天心软件 编辑:程序博客网 时间:2024/06/10 15:20

Problem Description
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,数据范围也不大,我们可以考虑O(n3)的区间dp。
解题的关键在于考虑对于一个区间,我们有怎样的消除方式?

【解题思路】
经过分析,我们可以得到这样的三种方式:
1:将这个区间分成左右两个部分,分别将左右两个区间消除完,两个区间互不干涉。
2:如果这个区间的两端是同色的,可以考虑将中间的所有球都消除完,然后通过左右的合并来消除整个区间,代价和左右两端同色球的数量有关。
3:如果这个区间的两段是同色的,而且中间有单独的一个球,和两端的颜色相同,这样可以考虑通过三部分的合并来消除。

对于第一种情况很好理解,然而第二第三种情况都有限定。
对于第二种情况,显然左右两端的同色球数量加起来要大于等于3。
对于第三种情况,左右两端的同色球加起来要小于4,因为若左右两段加起来大于等于4,而左右两段至少为1,说明至少有一端为2或以上,这样若将这一段中间的球消除,就会变成将这个区间分成两部分的情况,而不是我们所希望的“将中间消除剩下一个和左右两端一起消除”。

至此,此题得解。

【代码】

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int INF=0x3f;const int MAXN=205;char s[MAXN];int T,cnt;int len[MAXN],a[MAXN],f[MAXN][MAXN];inline void init(){    memset(f,INF,sizeof(f));    scanf("%s",s);    cnt=1;len[1]=1;    for(int i=1;i<strlen(s);++i)        if(s[i]==s[i-1])            ++len[cnt];        else            len[++cnt]=1;}inline void solve(){    for(int l=0;l<=cnt;++l)    {        for(int i=1;i<=cnt;++i)        {            int j=i+l;            if(j<1 || j>cnt)                continue;            if(l==0)                f[i][j]=3-len[i];            else            {                for(int k=i;k<j;++k)                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);                if((j-i-1)&1)                {                    if(len[i]+len[j]==2)                        f[i][j]=min(f[i][j],f[i+1][j-1]+1);                    else                        f[i][j]=min(f[i][j],f[i+1][j-1]);                    if(len[i]+len[j]<4)                        for(int k=i+2;k<j;k+=2)                            if(len[k]==1)                                f[i][j]=min(f[i][j],f[i+1][k-1]+f[k+1][j-1]);                }            }        }    }}int main(){    freopen("G.in","r",stdin);    freopen("G.out","w",stdout);    scanf("%d",&T);    for(int t=1;t<=T;++t)    {        init();        solve();        printf("Case #%d: %d\n",t,f[1][cnt]);    }    return 0;}
原创粉丝点击