題目描述
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;
}