zjut1547——自己想出来的动态规划





zjut1547传送门

Popstar》一款非常容易的消除类游戏,游戏的规则很简单:只需点击两个或两个以上颜色相同的方块即可消除。为了是题目更加简单,只有一维,而且一个方块也能消去,给你一行方块,每个方块都有一个颜色,每次都可以选取一段颜色相同的方块消去,得到消去数量的平方的分数,每次取玩后,消去方块的左右两边又会相互连在一起。问最多可以取得多少分?

                               

Input

有多组样例,每组样例先输入一个n,表示有n个方块,然后第二行有n个数字CiCi表示第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;
}	










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值