一道ACM题的求解--祖玛

Description

精致细腻的背景,外加神秘的印加音乐衬托,仿佛置身在古老国度里面,进行一个神秘的游戏——这就是著名的祖玛游戏。祖玛游戏的主角是一只石青蛙,石青蛙会吐出各种颜色的珠子,珠子造型美丽,并且有着神秘的色彩,环绕着石青蛙的是载着珠子的轨道,各种颜色的珠子会沿着轨道往前滑动,石青蛙必需遏止珠子们滚进去轨道终点的洞里头,如何减少珠子呢?就得要靠石青蛙吐出的珠子与轨道上的珠子相结合,颜色相同者即可以消失得分。直到轨道上的珠子通通都被清干净为止。

或许你并不了解祖玛游戏,没关系。这里我们介绍一个简单版本的祖玛游戏规则。一条通道中有一些玻璃珠,每个珠子有各自的颜色,如图1所示。玩家可以做的是选择一种颜色的珠子(注意:颜色可以任选,这与真实游戏是不同的)射入某个位置。


祖玛示意图(1)

中玩家选择一颗蓝色珠子,射入图示的位置,于是得到一个图3的局面。


祖玛示意图(2)


祖玛示意图(3)

当玩家射入一颗珠子后,如果射入的珠子与其他珠子组成了3颗以上连续相同颜色的珠子,这些珠子就会消失。例如,将一颗白色珠子射入图4的位置,就会产生3颗眼色相同的白色珠子。这3颗珠子就会消失,于是得到图5的局面。


祖玛示意图(4)


祖玛示意图(5)

需要注意的是,图1中的3颗连续的黄色珠子不会消失,因为并没有珠子射入其中。

珠子的消失还会产生连锁反应。当一串连续相同颜色的珠子消失后,如果消失位置左右的珠子颜色相同,并且长度大于2,则可以继续消失。例如,图6中,射入一颗红色珠子后,产生了3颗连续的红色珠子。当红色珠子消失后,它左右都是白色的珠子,并且一共有四颗,于是白色珠子也消失了。之后,消失位置的左右都是蓝色珠子,共有3颗,于是蓝色珠子也消失。最终得到图7的状态。

注意:图7中的3颗黄色珠子不会消失,因为蓝色珠子消失的位置一边是紫色珠子,另一边是黄色珠子,颜色不同。


祖玛示意图(6)


祖玛示意图(7)


除了上述的情况,没有其他的方法可以消去珠子。

现在,我们有一排珠子,需要你去消除。对于每一轮,你可以自由选择不同颜色的珠子,射入任意的位置。你的任务是射出最少的珠子,将全部珠子消去。

Input

第一行一个整数n(n ≤500),表示珠子的个数。第二行n个整数(32位整数范围内),用空格分割,每个整数表示一种颜色的珠子。

Output

一个整数,表示最少需要射出的珠子个数。

Sample Input

9

1 1 2 2 3 3 2 1 1

Sample Output

1




以下是代码,但是时间复杂度太高了...N>10基本就算不出来,如果有更好的算法可以给我留言~



/*	insert()方法:将串str的每个位置前插入一个相同颜色的珠子,消去后将剩余的串pstr再递归调用insert(),返回射出珠子数最小值
 * 	clear()方法:在i前射入同颜色珠子,返回经消除后的串
 *  类hasCount:存储计算过的str对应的minCount,以重复利用,这是后来加入的优化方案,以空间换时间
 */

import java.util.ArrayList;
import java.util.Scanner;




public class zuma {
	static ArrayList<hasCount> arr=new ArrayList<hasCount>();
	static int w=0;
	public static void main(String[] args){	
		Scanner sc=new Scanner(System.in);
		int n=sc.nextInt();
		int[] str=new int[n];
		for(int i=0;i<n;i++){
			str[i]=sc.nextInt();
		}
		int minCount=insert(str);
		System.out.println(minCount);
	}

	public static int insert(int[] str){	//再str每个位置之前射入珠子
		
		if(str.length==0) return 0;		//消除完毕
		int minCount=Integer.MAX_VALUE;

		for(int i=0;i<str.length;i++){
			if(i>0 && str[i]==str[i-1]) continue;
			int[] pstr=clear(str, i);
			int count=0;
			boolean flag=false;
			for(int j=0;j<arr.size();j++){		//查看是否已经计算过
				hasCount hc=(hasCount)arr.get(j);
				if(pstr.length!=hc.getStr().length) continue;
				int u=0;
				for(;u<pstr.length;u++){
					if(pstr[u]!=hc.getStr()[u]) break;
				}
				if(u==pstr.length){
					count=hc.getMinCount();
					flag=true;
					w++;
					break;
				}
			}

			if(!flag){		//递归insert计算并保存
				count=insert(pstr);
				if(pstr.length!=0){
					hasCount hc=new hasCount();
					hc.setStr(pstr);
					hc.setMinCount(count);
					arr.add(hc);
				}
			}
					
			if(minCount>count){
				minCount=count;
			}
		}

		return minCount+1;
	}
	
	public static int[] clear(int[] str, int i){	//在i处放入相同颜色的珠子后进行消除
		int p=i,q=i+1;	//左,右标记
		int count=0;	//重复颜色数
		int[] pstr;
		
		while(q<str.length && str[p]==str[q]){
			count++;
			q++;
		}
		
		if(count==0){		//一个珠子的情况
			pstr=new int[str.length+1];
			int index=0;
			for(int u=0;u<=p;u++){
				pstr[index++]=str[u];
			}
			pstr[index++]=str[p];
			for(int u=p+1;u<str.length;u++){
				pstr[index++]=str[u];
			}
			return pstr;
		}else{		//两个珠子的情况
			p--;
			count=3;
			int p2=0,q2=0;
			while(p>=0 && q<=str.length-1 && count>=3){
				p2=p;q2=q;	//标记原来的位置
				int count1=0,count2=0;
				count=0;
				int temp=str[p2];
				while(p2>=0 && str[p2]==temp){
					count1++;
					p2--;
				}
				while(q2<=str.length-1 && str[q2]==temp){
					count2++;
					q2++;
				}
				count=count1+count2;
				if(count1!=0 && count2!=0 && count>=3){
					p=p2;q=q2;
				}
			}
			
			
			
			int index=0;
			pstr=new int[str.length-(q-p-1)];
			for(int u=0;u<=p;u++){
				pstr[index++]=str[u];
			}
			
			for(int u=q;u<=str.length-1;u++){
				pstr[index++]=str[u];
			}
			
			if(index==0) pstr=new int[0];
			
			return pstr;
		}
	}
}


class hasCount{		//计算过的子串
	private int[] str=new int[10];
	private int minCount=0;
	
	public int[] getStr() {
		return str;
	}
	public void setStr(int[] str) {
		this.str=new int[str.length];
		for(int i=0;i<str.length;i++){
			this.str[i]=str[i];
		}
	}
	public int getMinCount() {
		return minCount;
	}
	public void setMinCount(int minCount) {
		this.minCount = minCount;
	}
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值