[NOIp2011] Mayan游戏

Description

Mayan puzzle是最近流行起来的一个游戏。游戏界面是一个\(7\)行×\(5\)列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:

1 、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图\(6\)到图\(7\));如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图\(1\)和图\(2\));

2 、任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则它们将立即被消除(参见图\(1\)到图\(3\))。

注意:

  • 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图\(4\),三个颜色为\(1\)的方块和三个颜色为\(2\)的方块会同时被消除,最后剩下一个颜色为\(2\)的方块)。

  • 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图\(5\)所示的情形,\(5\)个方块会同时被消除)。

  • 方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。

上面图\(1\)到图\(3\)给出了在棋盘上移动一块方块之后棋盘的变化。棋盘的左下角方块的坐标为(\(0\),\(0\)),将位于(\(3\),\(3\))的方块向左移动之后,游戏界面从图\(1\)变成图\(2\)所示的状态,此时在一竖列上有连续三块颜色为\(4\)的方块,满足消除条件,消除连续\(3\)块颜色为\(4\)的方块后,上方的颜色为\(3\)的方块掉落,形成图\(3\)所示的局面。

Input

\(6\)行。

第一行为一个正整数\(n\),表示要求游戏通关的步数。

接下来的\(5\)行,描述\(7\)×\(5\)的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个\(0\)结束,自下向上表示每竖列方块的颜色编号(颜色不多于\(10\)种,从\(1\)开始顺序编号,相同数字表示相同颜色)。

输入数据保证初始棋盘中没有可以消除的方块。

Output

如果有解决方案,输出\(n\)行,每行包含\(3\)个整数\(x\),\(y\),\(g\),表示一次移动,每两个整数之间用一个空格隔开,其中\((x,y)\)表示要移动的方块的坐标,\(g\)表示移动的方向,\(1\)表示向右移动,\(−1\)表示向左移动。注意:多组解时,按照\(x\)为第一关健字,\(y\)为第二关健字,\(1\)优先于\(-1\) ,给出一组字典序最小的解。游戏界面左下角的坐标为\((0,0)\)

如果没有解决方案,输出一行,包含一个整数\(−1\)

Sample Input

3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0

Sample Output

2 1 1
3 1 1
3 0 1

Hint

按箭头方向的顺序分别为图\(6\)到图\(11\)

样例输入的游戏局面如上面第一个图片所示,依次移动的三步是:\((2,1)\)处的方格向右移动,\((3,1)\)处的方格向右移动,\((3,0)\)处的方格向右移动,最后可以将棋盘上所有方块消除。

对于\(30%\)的数据,初始棋盘上的方块都在棋盘的最下面一行;

对于\(100%\)的数据,\(0<n≤5\)

\(noip2011\)提高组\(day1\)\(3\)

题解

无脑码农题,改错改了半天。最后不得不在\(Luogu\)下了如下数据才该对……

数据

数据一:

Input

2
1 0
1 0
0
1 0
1 0

Output

-1

数据二:

Input

5
1 1 2 3 4 0
3 3 1 2 3 5 4 0
3 5 5 3 2 2 0
5 5 3 5 2 4 2 0
0

Output

0 4 1
0 2 1
1 3 1
3 2 1
3 0 1

定义:

  • \(mp[][]\)记录读入的图,\(Step\)记录需要的步数

  • \(Res[][]\)记录每一步的状态,\(Ans[][]\)记录最终答案

  • \(flag\)标记是否找到答案,\(YH[]\)数组用于优化

const int maxn=15,maxstep=10,maxh=10,maxl=10;
int Step,mp[maxh][maxl],Res[maxstep][4];
int Ans[maxstep][4],flag,YH[maxn];

读入

读入很简单,但是第二层循环不要打成

for(int j=1;j<=7;++j)

因为有些输入输入了\(8\)个数,最后一项为\(0\)

void Read()
{
	scanf("%d",&Step);
	int num;
	for(int i=1;i<=5;++i)
		for(int j=1;;++j)
		{
			scanf("%d",&num);
			if(num) mp[i][j]=num;
			else break;
		}
}

下落

这个很简单,自己动手模拟一下就可以了:

void Change(int (*nw)[maxl])
{
	int cnt;
	for(int i=1;i<=5;++i)
	{
		cnt=0;
		for(int j=1;j<=7;++j)
			if(nw[i][j])
			{
				if(cnt) nw[i][j-cnt]=nw[i][j],nw[i][j]=0;
			}
			else ++cnt;
	}
}

清理

这个坑点很多,比如如果有四个、五个、……个连续的颜色是一起消除,还有图\(5\)所示的情况……

我用的是用一个数组记录原来的数组,再行与列分开清理。

bool Clean(int (*nw)[maxl])
{
	int tmp[maxh][maxl];
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j) tmp[i][j]=nw[i][j];
	int cnt; bool flag=0;
	for(int i=1;i<=5;++i)
	{
		cnt=0;
		for(int j=1;j<=7;++j)
		{
			if(tmp[i][j])
			{
				if(tmp[i][j]==tmp[i][j-1]) ++cnt;
				else cnt=1;
			}
			else cnt=0;
			if(cnt==3)
			{
				flag=1,
				nw[i][j-2]=nw[i][j-1]=nw[i][j]=0;
			}
			else if(cnt>3) nw[i][j]=0;
		}
	}
	for(int j=1;j<=7;++j)
	{
		cnt=0;
		for(int i=1;i<=5;++i)
		{
			if(tmp[i][j])
			{
				if(tmp[i][j]==tmp[i-1][j]) ++cnt;
				else cnt=1;
			}
			else cnt=0;
			if(cnt==3)
			{
				flag=1,
				nw[i-2][j]=nw[i-1][j]=nw[i][j]=0;
			}
			else if(cnt>3) nw[i][j]=0;
		}
	}
	return flag;
}

深搜

深搜部分码量大,我们一个一个看,首先我们看一个减枝:

bool Cut(int (*nw)[maxl])
{
	for(int i=0;i<maxn;++i) YH[i]=0;
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j) ++YH[nw[i][j]];
	for(int i=1;i<maxn;++i)
		if((YH[i])&&(YH[i]<3)) return 1;
	return 0;
}

因为如果一种方块小于\(3\)个的话,那么它绝对不会被消那么继续\(DFS\)下去肯定找不到解,当存在这种情况时,我们就返回真。

下面这个函数返回的是它的移动情况,返回值的意义如下:

  • 若为\(0\),则该数既不能左移,也不能右移

  • 若为\(1\),则该数只能左移

  • 若为\(2\),则该数只能右移

  • 若为\(3\),则该数既能左移,也能右移

设该数所处的位置为\((x,y)\)所有的情况如下:

  • \(x\)\(1\),则它不能左移;若\(x\)\(5\),则它不能右移

  • 仅仅只有\((x-1,y)\)的位置没数时,该数才能左移;因为若\((x-1,y)\)的位置有数,则\((x-1,y)\)的右移就等价于\((x,y)\)的左移,但是前者的字典序更小

  • \((x+1,y)\)的位置有数时,该数不需要右移

int Let_me_think_think(int x,int y,int nw[maxh][maxl])
{
	if(!nw[x][y]) return 0;
	if(x==1)
	{
		if(nw[x][y]!=nw[x+1][y]) return 1;
		return 0;
	}
	if(x==5)
	{
		if(nw[x-1][y]) return 0;
		return 2;
	}
	if(nw[x-1][y])
	{
		if(nw[x][y]!=nw[x+1][y]) return 1;
		return 0;
	}
	if(nw[x][y]!=nw[x+1][y]) return 3;
	return 2;
}

下面是一个简单的交换函数,不必解释

void Swap(int &a,int &b)
{
	int tmp;
	tmp=a,a=b,b=tmp;
}

最后就是很简单很麻烦的\(DFS\)

void Dfs(int res,int nw[maxh][maxl])
{
	if(flag) return;
	if(res>Step)
	{
		Check(nw); return;
	}
	if(Cut(nw)) return;
	int tmp[maxh][maxl],QAQ;
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j)
		{
			QAQ=Let_me_think_think(i,j,nw);
			if(!QAQ) continue;
			if(QAQ==1||QAQ==3)
			{
				for(int ii=1;ii<=5;++ii)
					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
				Res[res][1]=i,Res[res][2]=j,Res[res][3]=1;
				Swap(tmp[i][j],tmp[i+1][j]);
				Change(tmp);
				while(Clean(tmp)) Change(tmp);
				Dfs(res+1,tmp);
			}
			if(QAQ==2||QAQ==3)
			{
				for(int ii=1;ii<=5;++ii)
					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
				Res[res][1]=i,Res[res][2]=j,Res[res][3]=-1;
				Swap(tmp[i][j],tmp[i-1][j]);
				Change(tmp);
				while(Clean(tmp)) Change(tmp);
				Dfs(res+1,tmp);
			}
		}
}

整体代码:

#include<iostream>
#include<cstdio>
using namespace std;

const int maxn=15,maxstep=10,maxh=10,maxl=10;
int Step,mp[maxh][maxl],Res[maxstep][4];
int Ans[maxstep][4],flag,YH[maxn];

void Read()
{
	scanf("%d",&Step);
	int num;
	for(int i=1;i<=5;++i)
		for(int j=1;;++j)
		{
			scanf("%d",&num);
			if(num) mp[i][j]=num;
			else break;
		}
}

void Check(int nw[maxh][maxl])
{
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j)
			if(nw[i][j]) return;
	for(int i=1;i<=Step;++i)
		Ans[i][1]=Res[i][1],
		Ans[i][2]=Res[i][2],
		Ans[i][3]=Res[i][3];
	flag=1;
}

int Let_me_think_think(int x,int y,int nw[maxh][maxl])
{
	if(!nw[x][y]) return 0;
	if(x==1)
	{
		if(nw[x][y]!=nw[x+1][y]) return 1;
		return 0;
	}
	if(x==5)
	{
		if(nw[x-1][y]) return 0;
		return 2;
	}
	if(nw[x-1][y])
	{
		if(nw[x][y]!=nw[x+1][y]) return 1;
		return 0;
	}
	if(nw[x][y]!=nw[x+1][y]) return 3;
	return 2;
}

void Swap(int &a,int &b)
{
	int tmp;
	tmp=a,a=b,b=tmp;
}

void Change(int (*nw)[maxl])
{
	int cnt;
	for(int i=1;i<=5;++i)
	{
		cnt=0;
		for(int j=1;j<=7;++j)
			if(nw[i][j])
			{
				if(cnt) nw[i][j-cnt]=nw[i][j],nw[i][j]=0;
			}
			else ++cnt;
	}
}

bool Clean(int (*nw)[maxl])
{
	int tmp[maxh][maxl];
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j) tmp[i][j]=nw[i][j];
	int cnt; bool flag=0;
	for(int i=1;i<=5;++i)
	{
		cnt=0;
		for(int j=1;j<=7;++j)
		{
			if(tmp[i][j])
			{
				if(tmp[i][j]==tmp[i][j-1]) ++cnt;
				else cnt=1;
			}
			else cnt=0;
			if(cnt==3)
			{
				flag=1,
				nw[i][j-2]=nw[i][j-1]=nw[i][j]=0;
			}
			else if(cnt>3) nw[i][j]=0;
		}
	}
	for(int j=1;j<=7;++j)
	{
		cnt=0;
		for(int i=1;i<=5;++i)
		{
			if(tmp[i][j])
			{
				if(tmp[i][j]==tmp[i-1][j]) ++cnt;
				else cnt=1;
			}
			else cnt=0;
			if(cnt==3)
			{
				flag=1,
				nw[i-2][j]=nw[i-1][j]=nw[i][j]=0;
			}
			else if(cnt>3) nw[i][j]=0;
		}
	}
	return flag;
}

bool Cut(int (*nw)[maxl])
{
	for(int i=0;i<maxn;++i) YH[i]=0;
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j) ++YH[nw[i][j]];
	for(int i=1;i<maxn;++i)
		if((YH[i])&&(YH[i]<3)) return 1;
	return 0;
}

void Dfs(int res,int nw[maxh][maxl])
{
	if(flag) return;
	if(res>Step)
	{
		Check(nw); return;
	}
	if(Cut(nw)) return;
	int tmp[maxh][maxl],QAQ;
	for(int i=1;i<=5;++i)
		for(int j=1;j<=7;++j)
		{
			QAQ=Let_me_think_think(i,j,nw);
			if(!QAQ) continue;
			if(QAQ==1||QAQ==3)
			{
				for(int ii=1;ii<=5;++ii)
					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
				Res[res][1]=i,Res[res][2]=j,Res[res][3]=1;
				Swap(tmp[i][j],tmp[i+1][j]);
				Change(tmp);
				while(Clean(tmp)) Change(tmp);
				Dfs(res+1,tmp);
			}
			if(QAQ==2||QAQ==3)
			{
				for(int ii=1;ii<=5;++ii)
					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
				Res[res][1]=i,Res[res][2]=j,Res[res][3]=-1;
				Swap(tmp[i][j],tmp[i-1][j]);
				Change(tmp);
				while(Clean(tmp)) Change(tmp);
				Dfs(res+1,tmp);
			}
		}
}

void Print()
{
	if(!flag) {puts("-1");return;}
	for(int i=1;i<=Step;++i)
		printf("%d %d %d\n",Ans[i][1]-1,Ans[i][2]-1,Ans[i][3]);
}

int main()
{
	Read();
	Change(mp);
	while(Clean(mp)) Change(mp);
	Dfs(1,mp);
	Print();
	return 0;
}
posted @ 2019-08-24 20:05  OItby  阅读(183)  评论(0编辑  收藏  举报