题目描述
1、 每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图6 到图7);如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图1 和图2);
2、 任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则它们将立即被消除(参见图1 到图3)。
注意:
a) 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图4,三个颜色为1 的方块和三个颜色为2 的方块会同时被消除,最后剩下一个颜色为2 的方块)。
b) 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图5 所示的情形,5 个方块会同时被消除)。

3、 方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。
上面图1 到图3 给出了在棋盘上移动一块方块之后棋盘的变化。棋盘的左下角方块的坐标为(0, 0),将位于(3, 3)的方块向左移动之后,游戏界面从图1 变成图2 所示的状态,此时在一竖列上有连续三块颜色为4 的方块,满足消除条件,消除连续3 块颜色为4 的方块后,上方的颜色为3 的方块掉落,形成图3 所示的局面。
输入
第一行为一个正整数n,表示要求游戏通关的步数。
接下来的5 行,描述7*5 的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于10 种,从1 开始顺序编号,相同数字表示相同颜色)。
输入数据保证初始棋盘中没有可以消除的方块。
输出
如果没有解决方案,输出一行,包含一个整数-1。
样例输入
3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0
样例输出
2 1 1
3 1 1
3 0 1
提示
按箭头方向的顺序分别为图6 到图11
样例输入的游戏局面如上面第一个图片所示,依次移动的三步是:(2,1)处的方格向右移动,(3,1)处的方格向右移动,(3,0)处的方格向右移动,最后可以将棋盘上所有方块消除。
【数据范围】
对于30%的数据,初始棋盘上的方块都在棋盘的最下面一行;
对于100%的数据,0 < n≤5。
膜拜大佬300行之毅力,我只打了60行。。。
算法分析:dfs
dfs:对7*5中每个非0的点按照x从小到大,y从小到大,先右后左进行枚举,注意右边界点不能向右,左边界点不能向左,枚举时调用move函数并记录相应操作;每次枚举前要把原来的矩阵copy一份,便于回溯;当超过n时,判断是否为空,如果为空就输出记录的操作。具体解释见代码。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#define mem(a,b) memset(a,b,sizeof(a))
#define cop(a,b) memcpy(a,b,sizeof(b))
using namespace std;
int n,b[10][10],cnt,st[10],st2[10],st3[10];
bool ok[10][10];
bool wipe()//每次调用消除全图的所有可以消除的块
{
mem(ok,0);bool fu=0;//ok数组标记要消除的块,fu标记本次是否消除了块
//一定要标记不能直接删除,因为会有一个块为多次消除做贡献的情况
for(int i=1;i<=5;i++)
for(int j=1;j<=7;j++)//我把矩阵开到了10*10,所以不用考虑越界,多考虑的一定不影响结果
{ if(b[i][j]&&b[i][j]==b[i][j+1]&&b[i][j]==b[i][j+2]) ok[i][j]=ok[i][j+1]=ok[i][j+2]=1;
if(b[i][j]&&b[i][j]==b[i+1][j]&&b[i][j]==b[i+2][j]) ok[i][j]=ok[i+1][j]=ok[i+2][j]=1;
}
//每三个判断一次是否删除(注意判断的块要为真,都是0就不要消了)
for(int i=1;i<=5;i++) for(int j=1;j<=7;j++) if(ok[i][j]) b[i][j]=0,fu=1;
//把标记的块删除
return fu;//为了方便后面判断
}
void fall(){for(int i=1;i<=5;i++){int j=2;while(j<=7){while(j>1&&b[i][j]&&!b[i][j-1]) b[i][j-1]=b[i][j],b[i][j]=0,j--; j++;}}}
//将全地图的能降落的块降落,注意从下往上枚举(因为下面的块掉了会影响后面的块是否降落)
//还要注意细节(因为j>1写成了j>0调了一小时T.T)
bool check(){for(int i=1;i<=5;i++) for(int j=1;j<=7;j++) if(b[i][j]!=0) return 0; return 1;}
//判断地图是否为空
void print(){for(int i=1;i<=cnt;i++) printf("%d %d %d\n",st[i]-1,st2[i]-1,st3[i]);exit(0);}
//按步输出结果 并直接退出程序(exit(0)直接终止程序),省去回溯回去的过程
void move(int x,int y,int ai,int bi){int tmp=b[ai][bi];b[ai][bi]=b[x][y];b[x][y]=tmp;}
//就是交换x,y和ai,bi的位置(或许名字起得不好)
void mark(int x,int y,int z){st[cnt]=x;st2[cnt]=y;st3[cnt]=z;}//记录操作
void dfs()
{
if(cnt==n) if(check()) print();else return;//假如操作了n步,判断地图是否为空,为空就输出结果
for(int i=1;i<=5;i++)
for(int j=1;j<=7;j++)
{
if(!b[i][j]) break;//如果为空那么后面都为空,没必要再搜索了
int tmp[10][10];//备份矩阵
cop(tmp,b);//把b赋给tmp
if(i<5)//优先右移,i==5是不能右移
{ cnt++;move(i,j,i+1,j);mark(i,j,1);
while(1){fall();if(!wipe()) break;}//能消除一直消除,一定要先降落
dfs();cnt--;cop(b,tmp);//回溯回来返回之前状态
}
if(i>1&&!b[i-1][j])//剪枝,如果左边不为0的话一定会被左边向右搜回来,所以只有当左边为0时才有必要搜索
{ cnt++;move(i,j,i-1,j);mark(i,j,-1);
while(1){fall();if(!wipe()) break;}
dfs();cnt--;cop(b,tmp);//返回之前状态
}
}
}
int main()
{
//freopen("mayan.in","r",stdin);
//freopen("mayan.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=5;i++)//读入矩阵,注意不要读反,刚开始没看样例读错了...而且题目默认是从0开始的矩阵,所以我输出时都减了1
for(int j=1;;j++)
{ scanf("%d",&b[i][j]);
if(!b[i][j]) break;
}
dfs();
printf("-1");//如果到这里还没有结束程序那么一定无解,输出-1就好
return 0;
}