【NOIP 2011】Mayan游戲(搜索+模擬)


描述

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  ^-^


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM