[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;
}
本文作者:OItby @ https://www.cnblogs.com/hihocoder/
未经允许,请勿转载。