《Popstar》一款非常容易的消除类游戏,游戏的规则很简单:只需点击两个或两个以上颜色相同的方块即可消除。为了是题目更加简单,只有一维,而且一个方块也能消去,给你一行方块,每个方块都有一个颜色,每次都可以选取一段颜色相同的方块消去,得到消去数量的平方的分数,每次取玩后,消去方块的左右两边又会相互连在一起。问最多可以取得多少分?
Input
有多组样例,每组样例先输入一个n,表示有n个方块,然后第二行有n个数字Ci,Ci表示第i方块的颜色,(1<=Ci<=10), (1<= n <= 100)
Output
输出一行,每行一个数字,表示最大的分数。
Sample Input
5
1 2 2 1 1
3
1 1 1
3
1 2 3
Sample Output
13
9
这道题想了我整整两天呢!!!中间一度想放弃,没有题解的题目果然就是可怕~
先是用dfs来写,每个“同颜色“方块可以选择消和迟点消。
但是按照顺序的dfs有个很严重的问题,,若某块选择迟点消,但是后面一顿消除后,可能没有能与它结合的方块了,那和当初直接消有什么区别?这样导致复杂度比2^n都大了
不过我还是傻傻地写了出来,答案正确,但是数量到50以上就跑不出来了
于是苦思冥想一晚上,想到了动态规划,,很多的遍历过程,都是可以计算过,不必再计算的,比如 两个东西消,中间出来的值可以已知。
不过我不会表达这个,,,毕竟对动态规划还不熟
大概是这个公式吧:
将于前面与自己相同的数字结合相消的个数,记为sum_n
当前位置=start
结束位置=end
下1个将于自己结合的数字的位置=next_n
dp[ sum_n] [start][end]=MAX( (sum_n+own_n)^2+dp[0][start+1][end] , dp[sum_n+own_n][next_c][end]+dp[0][x+1][next_c-1]
#include<iostream>
#include<algorithm>
#include <stdio.h>
#include<stdlib.h>
using namespace std;
#define M 105
#define INF 9999999
struct star
{
int num,color,next;
};
star s[M];
int n,up_c[15],xie[105][105][105];
int dp(int n_sum,int x,int nn)
{
int next_c,xie_ans,heng_ans,best_ans,heng;
next_c=s[x].next;
if(x>=nn+1)
return 0;
if(xie[n_sum][x][nn]!=-1)
return xie[n_sum][x][nn];
xie_ans=(n_sum+s[x].num)*(n_sum+s[x].num) + dp(0,x+1,nn);
heng_ans=0;
if(next_c>nn||next_c==-1)
heng_ans=0;
else
{
while(next_c!=-1)
{
if(next_c>nn)
break;
heng=dp(n_sum+s[x].num,next_c,nn)+dp(0,x+1,next_c-1);
if(heng>heng_ans)
heng_ans=heng;
next_c=s[next_c].next;
}
}
best_ans=(xie_ans>heng_ans)?xie_ans:heng_ans;
xie[n_sum][x][nn]=best_ans;
return best_ans;
}
int main(void)
{
int i,j,ii,c,last_c,ans;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<=n+1;i++)
for(j=0;j<=n+1;j++)
for(ii=0;ii<=n+1;ii++)
xie[i][j][ii]=-1;
for(i=0;i<15;i++)
up_c[i]=-1;
last_c=-1;
j=0;
c=0;
for(i=1;i<=n;i++)
{
scanf("%d",&c);
if(c!=last_c)
{
j++;
s[j].color=c;
s[j].num=1;
if(up_c[c]!=-1) //upc存的是上一次颜色的坐标
s[up_c[c]].next=j;
up_c[c]=j;
}
else
{
s[j].num++;
}
last_c=c;
}
for(i=1;i<=11;i++)
s[up_c[i]].next=-1;
n=j;
ans=dp(0,1,n);
printf("%d\n",ans);
}
return 0;
}