题目描述
Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个7 行 ×5 列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:
- 每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图 6 到图 7);如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图 1 和图 2);
- 任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则它们将立即被消除(参见图1 到图3)。
注意:
a) 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图 4,三个颜色为 1 的方块和三个颜色为 2 的方块会同时被消除,最后剩下一个颜色为 2 的方块)。
b) 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图5 所示的情形,5 个方块会同时被消除)。
- 方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。
上面图 1 到图 3 给出了在棋盘上移动一块方块之后棋盘的变化。棋盘的左下角方块的坐标为 (0,0),将位于 (3,3) 的方块向左移动之后,游戏界面从图 1 变成图 2 所示的状态,此时在一竖列上有连续三块颜色为 4 的方块,满足消除条件,消除连续 3 块颜色为 4 的方块后,上方的颜色为 3 的方块掉落,形成图 3 所示的局面。
输入格式
共 6 行。
第一行为一个正整数 n,表示要求游戏通关的步数。
接下来的 5 行,描述 7×5 的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个 0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于 10 种,从 1 开始顺序编号,相同数字表示相同颜色)。
输入数据保证初始棋盘中没有可以消除的方块。
输出格式
如果有解决方案,输出 n 行,每行包含 3 个整数 x,y,g,表示一次移动,每两个整数之间用一个空格隔开,其中 (x,y) 表示要移动的方块的坐标,g 表示移动的方向,1 表示向右移动,−1 表示向左移动。注意:多组解时,按照 x 为第一关键字,y 为第二关键字,1 优先于 −1,给出一组字典序最小的解。游戏界面左下角的坐标为 (0,0)。
如果没有解决方案,输出一行 -1
。
输入输出样例
输入 #1
3 1 0 2 1 0 2 3 4 0 3 1 0 2 4 3 4 0
输出 #1
2 1 1 3 1 1 3 0 1
说明/提示
【输入输出样例说明】
按箭头方向的顺序分别为图 6 到图 11
样例输入的游戏局面如上面第一个图片所示,依次移动的三步是:(2,1) 处的方格向右移动,(3,1) 处的方格向右移动,(3,0) 处的方格向右移动,最后可以将棋盘上所有方块消除。
【数据范围】
对于 30% 的数据,初始棋盘上的方块都在棋盘的最下面一行;
对于 100% 的数据,0<n≤5。
题目分析
首先,这题一看肯定是个暴力搜索,因为题目中的变换/操作略复杂,所以我们尽量分开写函数解决每块小操作
注:本文中的地图变量 [ i ][ j ] 均代表自下而上第i行,自左而右第j列
首先把图用二维数组表示出来,结构体,图方便
struct node{int pad[10][10];}
题目中总共满足三个操作:
1.掉落
当一个方块下方没有方块时,会往下掉,直到到底或下方有方块
实现[x][y]方块的掉落
void drop(int x,int y,node &u){ //同步修改掉,用地址,下同
int r=x; //用来记录当前掉落块,接下来有用
if(u.pad[x][y]==0)return; //如果是个“空块”,不做了
bool flag=0; //用来判定这个块有没有掉落
while(u.pad[x-1][y]==0&&x>0)//下方为空且不是最底层
{
flag=1;
swap(u.pad[x-1][y],u.pad[x][y]);//掉落即交换
x--; //自己的位置降低
}
if(flag)drop(r+1,y,u); //如果自己掉了,自己上方也掉
}
2.横向拖动/交换
每个方块可以向左/右拖动,即与左/右交换方块,于是我们可以用简单的swap搞定
void move(int x,int y,int d,node &u){
swap(u.pad[x][y],u.pad[x][y+d]);//d是方向即1或-1,直接拿来用
drop(x+1,y,u);drop(x,y+d,u); //换来的可能是 0 ,换出去的一定不是零,会在宽搜时候保证
//我的上方方块可能会掉落,我过去的地方可能会使我掉落
}
3.消除
横向或纵向的三个连续相同块可以消掉,且可以共用块
因为有了共用块的性质,所以肯定不能无脑找到就消除,不妨打标记
void del(node &u){
bool need=0;
memset(vis,0,sizeof(vis)); //vis数组用来打标记
for(int i=0;i<7;i++) //从左下往右上搜
for(int j=0;j<5;j++) //只要考虑向右和向上的连续块即可
if(u.pad[i][j]!=0){ //存在方块
if(u.pad[i+1][j]==u.pad[i][j]&&u.pad[i+2][j]==u.pad[i][j])//纵向三个相连
vis[i][j]=vis[i+1][j]=vis[i+2][j]=1;
if(u.pad[i][j+1]==u.pad[i][j]&&u.pad[i][j+2]==u.pad[i][j])//横向三个相连
vis[i][j]=vis[i][j+1]=vis[i][j+2]=1;
}
for(int i=6;i>=0;i--) //消除的时候从上往下,可以同步drop操作
for(int j=0;j<5;j++)
if(vis[i][j]){ //被标记了要消除
u.pad[i][j]=0; //消掉
drop(i+1,j,u); //上方的方块下落
need=1;
}
if(need)del(u);
}
所以我们用一个need来判定有没有消除,如果有消除,那么再扫一遍,看在掉落后有没有新的可以消除的块
--------------------------------------------------------------------- 华丽丽的分割线
所有的操作解决,要消除完毕才算完成
一个check解决
bool check(node u){
for(int i=0;i<7;i++)
for(int j=0;j<5;j++)
if(u.pad[i][j]!=0)return false;//还有方块,失败
return true;
}
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,a,cnt[5];
bool vis[8][8],flag;
struct node{
short pad[8][8];
bool operator<(const node &a)const
{
for(int i(0);i<7;++i)
for(int j(0);j<5;++j){
if(pad[i][j]!=a.pad[i][j])return pad[i][j]<a.pad[i][j];
}
return false;
}
}s;
struct point{
node ground;
short step,mx[6],my[6];
int dir[6];
};
queue<point>line;
bool check(node u){
for(int i=0;i<7;i++)
for(int j=0;j<5;j++)
if(u.pad[i][j]!=0)return 0;
return 1;
}
void drop(int x,int y,node &u){
int r=x;
if(u.pad[x][y]==0)return;
bool flag=1;
while(u.pad[x-1][y]==0&&x>0)
{
flag=1;
u.pad[x-1][y]=u.pad[x][y];
u.pad[x][y]=0;
x--;
}
if(flag)drop(r+1,y,u);
}
void del(node &u){
bool need=0;
memset(vis,0,sizeof(vis));
for(int i=0;i<7;i++)
for(int j=0;j<5;j++)
if(u.pad[i][j]!=0){
if(u.pad[i+1][j]==u.pad[i][j]&&u.pad[i+2][j]==u.pad[i][j])
vis[i][j]=vis[i+1][j]=vis[i+2][j]=1;
if(u.pad[i][j+1]==u.pad[i][j]&&u.pad[i][j+2]==u.pad[i][j])
vis[i][j]=vis[i][j+1]=vis[i][j+2]=1;
}
for(int i=6;i>=0;i--)
for(int j=0;j<5;j++)
if(vis[i][j]){
u.pad[i][j]=0;
drop(i+1,j,u);
need=1;
}
if(need)del(u);
}
void move(int x,int y,int d,node &u){
swap(u.pad[x][y],u.pad[x][y+d]);
drop(x+1,y,u);drop(x,y+d,u);
}
void bfs(){
map<node,bool>used;
used[s]=1;
point e;
e.ground=s;
e.step=0;
line.push(e);
while(!line.empty())
{
point u=line.front(),v;
line.pop();
node back=u.ground;
for(int j=0;j<5;j++)
for(int i=0;i<7;i++)
{
if(back.pad[i][j]!=0)
{
if(j+1<5){
v=u;v.step++;
v.mx[v.step]=j,v.my[v.step]=i;
v.dir[v.step]=1;
move(i,j,1,v.ground);
del(v.ground);
if(v.step<n&&!used[v.ground]){line.push(v);used[v.ground]=1;}
if(v.step==n&&check(v.ground)){
for(int k=1;k<=n;k++)
cout<<v.mx[k]<<" "<<v.my[k]<<" "<<v.dir[k]<<endl;
flag=1;
return ;
}
}
if(j-1>=0&&back.pad[i][j-1]==0){
v=u;v.step++;
v.mx[v.step]=j,v.my[v.step]=i;
v.dir[v.step]=-1;
move(i,j,-1,v.ground);
del(v.ground);
if(v.step<n&&!used[v.ground]){line.push(v);used[v.ground]=1;}
if(v.step==n&&check(v.ground)){
for(int k=1;k<=n;k++)
cout<<v.mx[k]<<" "<<v.my[k]<<" "<<v.dir[k]<<endl;
flag=1;
return ;
}
}
}
}
}
return ;
}
int main(){
cin>>n;
for(int i=0;i<=4;i++)
{
while(cin>>a)
{
if(!a)break;
s.pad[cnt[i]++][i]=a;
}
}
bfs();
if(!flag)printf("-1");
return 0;
}