noip2011 Mayan游戏

题目链接:Mayan游戏


这题我首先想到的是bfs,但因为bfs的内存消耗太大,所以只能用dfs。dfs的递归深度只有5层,所以可以考虑。


关于消除方块的问题,不能找到3个相同的方块就消除,如果这样遇到两组共一个方块的就会出错(如题图5)。所以,必需把所有可以消除的方块都先找出来,再消除。因为可能存在连着消除的情况,所以外层要加一个循环。


关于方块下落,最开始交换完就下落一次,在删除方块的过程中,每删除一轮(最外层循环一次)就下落一次。


关于复习运算符重载:

写在结构体内部:

    函数的()与{}之间要加上const,只需传和当前结构体变量运算的那个参数够了(如重载x<y时只用传y)。

    例:

bool operator < (const point& yy) const {
	if(x<yy.x) return true;
	if(x==yy.x&&y<yy.y) return true;
	return false;
}

写在结构体外:

    函数的()与{}之间不加const,运算所需的所有参数都得传(如重载x<y时x,y都要传)。

    例:

bool operator < (const point& xx,const point& yy){
	if(xx.x<yy.x) return true;
	if(xx.x==yy.x&&xx.y<yy.y) return true;
	return false;
}


关于dfs判重;

要注意某一种情况在比当前层数深的地方出现后,在当前情况中还是可以用的,不能在判重中将其去掉。


关于dfs减枝:

1、两个空格不交换。

2、点x和点y交换与点y和点x交换无区别,所以根据优先级关系只考虑像右边交换的情况。而当某个点左边为0时要考虑像左交换。

3、在某种情况下,如果某一种颜色的数量等于1或2,则无论如何这种颜色都不会被消除。

4、如果两个方块颜色相同,不交换。


代码:

#include<iostream>
#include<cstdio>
#include<set>
#include<map>
#include<cstring>
using namespace std;

#define n 5
#define m 7

int c;

struct G {
	int a[10][10];
	int step;
	bool empty() {
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=m; j++) {
				if(a[i][j]!=0)
					return false;
			}
		}
		return true;
	}
	bool operator < (const G& yy) const {
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=m; j++) {
				if(a[i][j]<yy.a[i][j]) return true;
				if(a[i][j]>yy.a[i][j]) return false;
			}
		}
		return false;
	}
};

struct point {
	int x,y,g;
	point() {}
	point(int xx,int yy,int gg) {
		x=xx,y=yy,g=gg;
	}
	bool operator < (const point& yy) const {
		if(x<yy.x) return true;
		if(x==yy.x&&y<yy.y) return true;
		return false;
	}
	bool operator == (const point& yy) const {
		if(x==yy.x&&y==yy.y&&g==yy.g) return true;
		return false;
	}
};
point move[10];

void Swap(int& x,int& y) {
	int t=x;
	x=y;
	y=t;
	return ;
}

void drop(G& x) {
	for(int i=1; i<=n; i++) {
		for(int j=m; j>=1; j--) {
			if(x.a[i][j]==0) {
				for(int k=j; k<=m; k++) {
					x.a[i][k]=x.a[i][k+1];
				}
			}
		}
	}

	bool flag1;
	do {
		flag1=false;
		set<point> Set;
		for(int i=1; i<=n; i++) { 
			for(int j=1; j<=m-2; j++) {
				if(x.a[i][j]!=0&&x.a[i][j]==x.a[i][j-1]&&x.a[i][j]==x.a[i][j-2]) {
					Set.insert(point(i,j,0));
					Set.insert(point(i,j-1,0));
					Set.insert(point(i,j-2,0));
					flag1=true;
				}
			}
		}
		for(int i=3; i<=n; i++) {
			for(int j=1; j<=m; j++) {
				if(x.a[i][j]!=0&&x.a[i][j]==x.a[i-1][j]&&x.a[i][j]==x.a[i-2][j]) {
					Set.insert(point(i,j,0));
					Set.insert(point(i-1,j,0));
					Set.insert(point(i-2,j,0));
					flag1=true;
				}
			}
		}
		for(set<point>::iterator it=Set.begin(); it!=Set.end(); it++) {
			x.a[(*it).x][(*it).y]=0;
		}
		for(int i=1; i<=n; i++) {
			for(int j=m; j>=1; j--) {
				if(x.a[i][j]==0) {
					for(int k=j; k<=m; k++) {
						x.a[i][k]=x.a[i][k+1];
					}
				}
			}
		}
	} while(flag1);

	return ;
}

bool flag=false;
map<G,bool> mp;
void dfs(G x) {
	drop(x);
	if(x.step>=c) {
		if(x.empty()) {
			for(int i=0; i<c; i++) { 
				printf("%d %d %d\n",move[i].x-1,move[i].y-1,move[i].g);
			}
			flag=true;
		}
		return ;
	}
	int scolor[11]={0};
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scolor[x.a[i][j]]++;
		}
	}
	for(int i=1;i<=9;i++){
		if(scolor[i]==1||scolor[i]==2) return ;
	}
	
	int t=x.step;
	x.step=0;
	if(mp.count(x)&&mp[x]==true) return ;
	mp[x]=true;
	x.step=t;
	
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=m; j++) {
			if(x.a[i][j]==0) continue;
			G y=x;
			if(i+1<=n&&y.a[i][j]!=y.a[i+1][j]) {
				move[x.step]=point(i,j,1);
				swap(y.a[i][j],y.a[i+1][j]);
				y.step++;
				dfs(y);
				if(flag==true) return ;
			}
			y=x;
			if(i-1>=1&&y.a[i-1][j]==0) {
				move[x.step]=point(i,j,-1);
				swap(y.a[i][j],y.a[i-1][j]);
				y.step++;
				dfs(y);
				if(flag==true) return ;
			}

		}
	}

	mp[x]=false;
	return;

}

int main() {
	scanf("%d",&c);
	G g;
	memset(g.a,0,sizeof(g.a));
	g.step=0;
	for(int i=1; i<=n; i++) {
		int x=1;
		for(int j=1; x!=0; j++) {
			scanf("%d",&x);
			g.a[i][j]=x;
		}
	}
	g.step=0;
	dfs(g);
	if(flag==false) printf("-1");
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值