描述
Mayan puzzle是最近流行起來的一個游戲。游戲界面是一個7行5列的棋盤,上面堆放着一些方塊,方塊不能懸空堆放,即方塊必須放在最下面一行,或者放在其他方塊之上。**游戲通關是指在規定的步數內消除所有的方塊,**消除方塊的規則如下:
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開始順序編號,相同數字表示相同顏色)。
輸入數據保證初始棋盤中沒有可以消除的方塊。
輸出格式
如果有解決方案,輸出n行,每行包含3個整數x,y,g,表示一次移動,每兩個整數之間用一個空格隔開,其中(x,y)表示要移動的方塊的坐標,g表示移動的方向,1表示向右移動,-1表示向左移動。**注意:多組解時,按照x為第一關鍵字,y為第二關鍵字,1優先於-1,給出一組字典序最小的解。游戲界面左下角的坐標為(0, 0)。**
如果沒有解決方案,輸出一行,包含一個整數-1。
樣例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
限制
3s
提示
樣例輸入的游戲局面如圖6到圖11所示。依次移動的三步是:(2,1)處的方格向右移動,(3,1)處的方格向右移動,(3,0)處的方格向右移動,最后可以將棋盤上所有方塊消除。
數據規模如下:
對於30%的數據,初始棋盤上的方塊都在棋盤的最下面一行;
對於100%的數據,0 < n ≤ 5。
題解
此題就相當與是一道大型搜索模擬題,需要耐心,極考驗碼力;
因為代碼比較長,所以建議大家多寫函數,盡量不要都寫在一起;
提前交代數組
int map[N][N]; //輸入的圖 int ans[N][5]; //輸出的答案 int last[N][N][N]; //后面會講 bool xiao[N][N]; //后面會講
下面我講一下其中的核心函數:
1.copy(復制):
我們要把當前的原始狀態復制
為什么要復制呢,回溯時要用;
但不能使用二維數組了,要定義一個三維數組
last[d][i][j]:第d步時在i行j列的原狀態;
void copy(int x){ for(int i=1;i<=5;i++) for(int j=1;j<=7;j++) last[x][i][j]=map[i][j]; }
2.update(更新游戲的狀態):
這個比較簡單,就是把該掉下去的掉下去;
定義一個x為這個這個方塊下0的個數,然后模擬一下;
void update(){ for(int i=1;i<=5;i++){ int x=0; for(int j=1;j<=7;j++){ if(!map[i][j])x++; else{ map[i][j-x]=map[i][j]; map[i][j]=0; } } } }
3.remove(消除):
題目要求一定要行或列連續3個才能消除;
但一定不能遇到3個連續的就消;
例如:
圖5中的要是先消3個,那剩下的就不能消了,就WA了;
我枚舉的i,j是中間方塊的坐標;
而且使用的是bool型,為了后面判斷是否可以繼續去消;
bool remove(){ int flag=0; for(int i=1;i<=5;i++) for(int j=1;j<=7;j++){ if(i-1>=1&&i+1<=5&&map[i][j]==map[i-1][j]&&map[i][j]==map[i+1][j]&&map[i][j]){ xiao[i-1][j]=1;xiao[i+1][j]=1;xiao[i][j]=1;flag=1; } if(j-1>=1&&j+1<=7&&map[i][j]==map[i][j+1]&&map[i][j]==map[i][j-1]&&map[i][j]){ xiao[i][j]=1;xiao[i][j+1]=1;xiao[i][j-1]=1;flag=1; } } if(!flag)return 0; for(int i=1;i<=5;i++) for(int j=1;j<=7;j++) if(xiao[i][j]){ xiao[i][j]=0; map[i][j]=0; } return 1; }
4.move (移動):
移動比較簡單;就是用到了之前函數;
要注意,可能消除后還可以更新,所以要使用while循環;
void move(int i,int j,int x){ int tmp=map[i][j]; map[i][j]=map[i+x][j]; map[i+x][j]=tmp; update(); while(remove())update(); }
5.check(判斷是否都消除了):
這個更簡單了;
因為所有方塊都掉落了,所以直接判斷最后一行都為0就行了;
bool check(){ for(int i=1;i<=5;i++) if(map[i][1])return 0; return 1; }
DFS的剪枝:
1.相同顏色的方塊可以跳過(顯而易見);
2.還有一個比較難想的剪枝:
結論:只有右邊有方塊才move,左邊沒有方塊才move;
證明(自己瞎寫的):
(你正在搜i列)若左面有方塊,那么你會在搜i-1列時將其右移,和你在i列時左移是等效的,所以可以減掉;
code:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cctype> 6 #include<cstdlib> 7 #define ll long long 8 #define N 10 9 using namespace std; 10 int read() 11 { 12 int X=0,w=0; char ch=0; 13 while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} 14 while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); 15 return w?-X:X; 16 } 17 int n,map[N][N],ans[N][5],last[N][N][N],xiao[N][N]; 18 bool remove(){ 19 int flag=0; 20 for(int i=1;i<=5;i++) 21 for(int j=1;j<=7;j++){ 22 if(i-1>=1&&i+1<=5&&map[i][j]==map[i-1][j]&&map[i][j]==map[i+1][j]&&map[i][j]){ 23 xiao[i-1][j]=1;xiao[i+1][j]=1;xiao[i][j]=1;flag=1; 24 } 25 if(j-1>=1&&j+1<=7&&map[i][j]==map[i][j+1]&&map[i][j]==map[i][j-1]&&map[i][j]){ 26 xiao[i][j]=1;xiao[i][j+1]=1;xiao[i][j-1]=1;flag=1; 27 } 28 } 29 if(!flag)return 0; 30 for(int i=1;i<=5;i++) 31 for(int j=1;j<=7;j++) 32 if(xiao[i][j]){ 33 xiao[i][j]=0; 34 map[i][j]=0; 35 } 36 return 1; 37 } 38 39 bool check(){ 40 for(int i=1;i<=5;i++) 41 if(map[i][1])return 0; 42 return 1; 43 } 44 void copy(int x){ 45 for(int i=1;i<=5;i++) 46 for(int j=1;j<=7;j++) 47 last[x][i][j]=map[i][j]; 48 } 49 void update(){ 50 for(int i=1;i<=5;i++){ 51 int wow=0; 52 for(int j=1;j<=7;j++){ 53 if(!map[i][j])wow++; 54 else{ 55 if(!wow)continue; 56 map[i][j-wow]=map[i][j]; 57 map[i][j]=0; 58 } 59 } 60 } 61 } 62 void move(int i,int j,int x){ 63 int tmp=map[i][j]; 64 map[i][j]=map[i+x][j]; 65 map[i+x][j]=tmp; 66 update(); 67 while(remove())update(); 68 } 69 70 void dfs(int x){ 71 if(check()){ 72 for(int i=1;i<=n;i++){ 73 if(i!=1)printf("\n"); 74 for(int j=1;j<=3;j++) 75 printf("%d ",ans[i][j]); 76 } 77 exit(0); 78 } 79 if(x==n+1)return; 80 copy(x); 81 for(int i=1;i<=5;i++) 82 for(int j=1;j<=7;j++){ 83 if(map[i][j]){ 84 if(i+1<=5&&map[i][j]!=map[i+1][j]){ 85 move(i,j,1); 86 ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=1; 87 dfs(x+1); 88 for(int i=1;i<=5;i++) 89 for(int j=1;j<=7;j++) 90 map[i][j]=last[x][i][j]; 91 ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1; 92 } 93 if(i-1>=1&&map[i-1][j]==0){ 94 move(i,j,-1); 95 ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=-1; 96 dfs(x+1); 97 for(int i=1;i<=5;i++) 98 for(int j=1;j<=7;j++) 99 map[i][j]=last[x][i][j]; 100 ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1; 101 } 102 } 103 } 104 } 105 int main() 106 { 107 // freopen("Manya.in","r",stdin); 108 // freopen("Manya.out","w",stdout); 109 n=read(); 110 for(int i=1;i<=5;i++){ 111 for(int j=1;j<=8;j++){ 112 int x=read(); 113 if(x==0)break; 114 map[i][j]=x; 115 } 116 } 117 memset(ans,-1,sizeof(ans)); 118 dfs(1); 119 puts("-1"); 120 return 0; 121 }
——ZAGER ^-^