POJ1390 方盒游戏

N个方盒(box)摆成一排,每个方盒有自己的颜色。连续摆放的同颜色方盒构成一个方盒片段(box segment)。下图中共有四个方盒片段,每个方盒片段分别有1、4、3、1个方盒


玩家每次点击一个方盒,则该方盒所在方盒片段就会消失。若消失的方盒片段中共有k个方盒,则玩家获得k*k个积分。

请问:给定游戏开始时的状态,玩家可获得的最高积分是多少?

Input

第一行是一个整数t(1<=t<=15),表示共有多少组测试数据。每组测试数据包括两行
第一行是一个整数n(1<=n<=200),,表示共有多少个方盒
第二行包括n个整数,表示每个方盒的颜色。这些整数的取值范围是[1 n]
output:

对每组测试数据,分别输出该组测试数据的序号、以及玩家可以获得的最高积分

思路:

将连续的若干个方块作为一个“大块”(box_segment) 考虑,假设开始一共有 n个“大块”,编号0到n-1 第i个大块的颜色是 color[i],包含的方块数目,即长度,是len[i]

明显的递归问题:先走一步,若先看最后一块,有两种方案,然后取最优者:

1)直接点击,然后前面的方块又是一个新的队列。

2)先保留,使该方块与前面同色大块的集合,

对于2),左边的同色大块可能有多个,找到它只能枚举,找到k,与它合并。找到之后,但不是click_box(i,k-1) + click_box(k+1,j-1) + (len[k]+len[j])2。

因为将大块 k和大块j合并后,形成的新大块会在最右边。将该新大块直接将其消去的做法,才符合上述式子,但直接将其消去,未必是最好的,也许它还应该和左边的同色大块合并,才更好。

考虑新的形式:
click_box(i,j,ex_len)
表示:
大块j的右边已经有一个长度为ex_len的大块(该大块可能是在合并过程中形成的,不妨就称其为ex_len),且j的颜色和ex_len相同,在此情况下将 i 到j以及ex_len都消除所能得到的最高分。
于是整个问题就是求:click_box(0,n-1,0)。

求click_box(i,j,ex_len)时,有两种处理方法,取最优者
假设j和ex_len合并后的大块称作 Q
1) 将Q直接消除,这种做法能得到的最高分就是:
click_box(i,j-1,0) + (len[j]+ex_len)2
2) 期待Q以后能和左边的某个同色大块合并。需要枚举可能和Q合并的大块。假设让大块k和Q合并,则此时能得到的最大分数是:
click_box(i,k,len[j]+ex_len) + click_box(k+1,j-1,0)

click_box(i,j,ex_len) 递归的终止条件:
i == j

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int p=210;
struct Node{
    int color;
    int len;
}node[p];
int score[p][p][p],r,result,k;
int Clickbox(int i,int j,int len){
    if(score[i][j][len]!=-1)
        return score[i][j][len];
    result=(node[j].len+len)*(node[j].len+len);
    if(i==j)
         return result;
    result+=Clickbox(i,j-1,0);//直接点击
    for(int k=i;k<=j-1;k++){//枚举k的位置,找到与j相同颜色的位置
        if(node[k].color!=node[j].color)
            continue;
            r=Clickbox(k+1,j-1,0);
            r+=Clickbox(i,k,node[j].len+len);//先与左边同颜色的结合但不点击,企图再与左边同颜色的方块结合
            result=max(r,result);//取直接点击和集合的最大值
    }
    score[i][j][len]=result;
    return result;
}
int main(){
    int m,n,a,num;
    scanf("%d",&m);
    for(int j=1;j<=m;j++){
        num=-1;
        memset(score,0xff,sizeof(score));
        scanf("%d",&n);
        int lasta=0;
        for(int i=0;i<n;i++){
            scanf("%d",&a);
            if(a!=lasta){
                num++;//表示颜色的种类
                node[num].len=1;
                node[num].color=a;
                lasta=a;
            }
            else{
                node[num].len++;
            }
        }
        printf("Case%d:%d\n",j,Clickbox(0,num,0));
    }
}

猜你喜欢

转载自blog.csdn.net/lijunyan5/article/details/81669208
今日推荐