HDU 4012 Paint on a Wall 搜索

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove

对于2*n的矩形,每次能选择子矩形进行染色,问达到最终要求的最小步数。

每一步选择一个位置,进行扩展,将尽可能大的部分进行标记,如果遇到已经和目标颜色一样的位置,则跳出。最后把每一步的所有子集都加入到队列当中,总共最多16个方格,如果与目标颜色一样用1表示,否则为0,这样使用一个16位的二进制数便可以保存。

对于子集来说,每次减一再与便可以,这样可以每次减少一个1。

扩展包括两种情况,一种是单行的,往两边扩展。还有一种是双行的,只需要考虑上一行为中心即可,因为下一行的情况已经考虑。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<string>
using namespace std;
struct Node{
	int val,step;    //当前情况与步数
}s,e,u,v;
char str[20];
bool flag[1<<16];   //16位二进制数表示,是否与目标颜色一致
int n;
int bfs(){
	queue<Node>que;
	while(!que.empty())
		que.pop();
	que.push(s);
	memset(flag,false,sizeof(flag));
	flag[0]=true;
	while(!que.empty()){
		u=que.front();
		que.pop();
		if(u.val==(1<<(2*n))-1)   //所有位置全部和目标颜色一样
			return u.step;
		for(int i=0;i<2*n;i++){
			v=u;v.step++;
			if((1<<i)&u.val) continue;   //当前已经为目标色
			int tmp=0;
			for(int j=i;j<(i/n+1)*n;j++){   //单行往右扩展
				if((1<<j)&u.val)
					break;
				if(str[j]==str[i]) tmp|=1<<j;   
			}
			for(int j=i-1;j>=(i/n)*n;j--){  //单行往左扩展
				if((1<<j)&u.val)
					break;
				if(str[j]==str[i]) tmp|=1<<j;
			}
			for(int j=tmp;j;j=tmp&(j-1)){  //将所有子集添加
				if(flag[u.val|j])
					break;
				flag[u.val|j]=true;
				v.val=(u.val|j);			
				que.push(v);
			}
			if(i>=n)   //只需要考虑第一行,避免还要分情况考虑
				continue;
			if(u.val&(1<<(i+n)))
				continue;
			tmp=0;
			for(int j=i;j<n;j++){
				if(((1<<j)&u.val)||((1<<(j+n))&u.val))
					break;	
				if(str[j]==str[i]) tmp|=(1<<j);
				if(str[j+n]==str[i]) tmp|=(1<<(j+n));
			}
			for(int j=i-1;j>=0;j--){
				if(((1<<j)&u.val)||((1<<(j+n))&u.val))
					break;
				if(str[j]==str[i]) tmp|=(1<<j);
				if(str[j+n]==str[i]) tmp|=(1<<(j+n));
			}
			for(int j=tmp;j;j=tmp&(j-1)){
				if(flag[u.val|j])
					continue;
				flag[u.val|j]=true;
				v.val=(u.val|j);
				que.push(v);
			}
		}
	}
}
int main(){
	int t,cas=0;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		scanf("%s%s",str,str+n);
		s.val=s.step=0;
		printf("Case #%d: %d\n",++cas,bfs());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值