洛谷P1312 Mayan遊戲 DFS+剪枝(3s搜爆!)

題目鏈接
題目描述 Description
Mayan puzzle 是最近流行起來的一個遊戲。遊戲界面是一個7 行5 列的棋盤,上面堆放
着一些方塊,方塊不能懸空堆放,即方塊必須放在最下面一行,或者放在其他方塊之上。遊
戲通關是指在規定的步數內消除所有的方塊,消除方塊的規則如下:
1、每步移動可以且僅可以沿橫向(即向左或向右)拖動某一方塊一格:當拖動這一方
塊時,如果拖動後到達的位置(以下稱目標位置)也有方塊,那麼這兩個方塊將交換位置(參
見輸入輸出樣例說明中的圖6 到圖7);如果目標位置上沒有方塊,那麼被拖動的方塊將從
原來的豎列中抽出,並從目標位置上掉落
這裏寫圖片描述
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 所示的局面。
輸入描述 Input Description
共6 行。
第一行爲一個正整數 n,表示要求遊戲通關的步數。
接下來的 5 行,描述7*5 的遊戲界面。每行若干個整數,每兩個整數之間用一個空格隔開,每行以一個0 結束,自下向上表示每豎列方塊的顏色編號(顏色不多於10 種,從1 開始順序編號,相同數字表示相同顏色)。
輸入數據保證初始棋盤中沒有可以消除的方塊。
輸出描述 Output Description
如果有解決方案,輸出n 行,每行包含3 個整數x,y,g,表示一次移動,每兩個整數之間用一個空格隔開,其中(x,y)表示要移動的方塊的座標,g 表示移動的方向,1 表示向右移動,-1 表示向左移動。注意:多組解時,按照x 爲第一關健字,y 爲第二關健字,1優先於-1,給出一組字典序最小的解。遊戲界面左下角的座標爲(0,0)。
如果沒有解決方案,輸出一行,包含一個整數-1。
水題分析 Waterproblem Analysis
這道題前前後後推到重打打了三遍,前前後後打了一天半,中間棄了一段時間做了幾個模板題放鬆心情,經歷了無數次的絕望,沒想到最後還是給a了只不過自己太蠢時間複雜度太大。打一個這樣的題都用這麼長時間,NOIplus 2017咋辦啊!!!一開始就是準備練搜索才做的這一個題,一開始打的是BFS,但是後來只得了40分,以爲狀態實在是太多了有70^5這麼多吧,隊列開的太大了就MLE了開的太小了狀態保存不了,即使用了循環隊列也把之前的狀態給替代掉了搜出來就是WA了。這樣就重打了兩遍第一遍0分TLE+RE,第二遍40分,才發現BFS不是我能處理的了的。博遠大佬太神了,巧妙的運用了dfs回溯之後答案互補影響的特點,只開了一個結構體節約了很多的空間。這樣隨便打打就可以在洛谷上拿70分了。
這道題我在3個評測網站上測,vijos上ac,codevs上90,洛谷70。我不知道是洛谷數據強還是香港記者號跑不動了,洛谷t了3個點。
介紹幾個顯而易見的剪枝:
1. 當我們在進行搜索的時候如果交換的雙方顏色一樣我們可以不進行交換,他們對答案的貢獻只有步數增加,而這種情況能成爲最終答案的情況少之又少,所以我們可以進行剪枝。
2. 在進行判斷是否爲答案時,只需要判斷最後一行即可,因爲我們有down的操作。
3. 對於我們每進行的一次操作之後的地圖統計色塊數量如果有一種顏色的數量大於0且小於3那麼直接return返回上一層,因爲這種情況無論如何我們也不可能得到我們想要的答案。
4. 題目中有說向右移動的操作優先級要高於向左移動,沒有認真思考這句話導致一個點一直WA,對於這個優先級,又可以進行另一個剪枝。如果座標(x,y)與座標(x+1,y)都是有方塊的座標,實際上從(x,y)向右移與從(x+1,y)向左移取得的效果是一樣的,所以如果左移的話,他對答案的實際貢獻只有他的左邊的座標爲空時。這樣我們在左移時進行判斷即可。
介紹一下我認爲這個題目中比較難處理的一些操作,方塊進行位置交換後,爲了保證下一次交換的準確,那麼我們就要對當前的地圖進行處理。
我這裏用了兩個函數clear_up()與down()。clear_up()是對當前地圖進行消除工作,而down()則是對於那些違反萬有引力的方塊進行因爲受地球重力而進行下落的操作。附上代碼:

void clear_up(int x)
{
    flag=0;
    memset(v,0,sizeof(v));
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            int color=q[x].map[i][j];
            if(color==0)continue;
            for(int k=1;k<=4;k++)
            {
                int tx=i+fx[k],ty=j+fy[k];
                int txx=tx+fx[k],tyy=ty+fy[k];
                if(q[x].map[tx][ty]==color&&q[x].map[txx][tyy]==color)
                {
                    if(tx<5&&tx>=0&&ty<7&&ty>=0&&txx>=0&&txx<5&&tyy>=0&&tyy<7)
                    {
                        flag=1;
                        v[tx][ty]=1;
                        v[i][j]=1;
                        v[txx][tyy]=1;
                    }
                }                
            }
        }
    }
    kuan=0;
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            if(v[i][j])
            {
                q[x].map[i][j]=0;
            }
        }
    }
}
void down(int x)
{
    for(int i=0;i<5;i++)
    {
        int sep=0,to=0;
        for(int j=0;j<7;j++)
        {
            if(q[x].map[i][j]==0)
            {
                sep=j;
                break;
            }
        }
        to=sep;
        for(int j=sep;j<7;j++)
        {
            if(q[x].map[i][j]!=0)
            {
                to=j;
                break;
            }
        }
        if(sep==0&&to==0)continue;
        for(int j=to;j<7;j++)
        {
            q[x].map[i][sep]=q[x].map[i][j];
            q[x].map[i][j]=0;
            sep++; 
        }
    }
}

這樣我感覺這個題就沒有什麼難度了,仔細調試的話,應該都可以AC了附上完整代碼:

#include<iostream>
#include<cstring>
using namespace std;
struct re
{
    int map[10][10],sp[3];
}q[110];
int w[111111];
int n,kuan=0;
bool v[10][10];
int fx[5]={0,-1,1,0,0};
int fy[5]={0,0,0,-1,1}; 
bool ans=0,flag=0;
bool cheak(int x)
{
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        if(q[x].map[i][j]!=0)return 0;
    } 
    return 1;
}
void clear_up(int x)
{
    flag=0;
    memset(v,0,sizeof(v));
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            int color=q[x].map[i][j];
            if(color==0)continue;
            for(int k=1;k<=4;k++)
            {
                int tx=i+fx[k],ty=j+fy[k];
                int txx=tx+fx[k],tyy=ty+fy[k];
                if(q[x].map[tx][ty]==color&&q[x].map[txx][tyy]==color)
                {
                    if(tx<5&&tx>=0&&ty<7&&ty>=0&&txx>=0&&txx<5&&tyy>=0&&tyy<7)
                    {
                        flag=1;
                        v[tx][ty]=1;
                        v[i][j]=1;
                        v[txx][tyy]=1;
                    }
                }                
            }
        }
    }
    kuan=0;
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            if(v[i][j])
            {
                q[x].map[i][j]=0;
            }
        }
    }
}
void down(int x)
{
    for(int i=0;i<5;i++)
    {
        int sep=0,to=0;
        for(int j=0;j<7;j++)
        {
            if(q[x].map[i][j]==0)
            {
                sep=j;
                break;
            }
        }
        to=sep;
        for(int j=sep;j<7;j++)
        {
            if(q[x].map[i][j]!=0)
            {
                to=j;
                break;
            }
        }
        if(sep==0&&to==0)continue;
        for(int j=to;j<7;j++)
        {
            q[x].map[i][sep]=q[x].map[i][j];
            q[x].map[i][j]=0;
            sep++; 
        }
    }
}
void make_map(int x,int tx,int ty,int go)
{
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            q[x].map[i][j]=q[x-1].map[i][j];
        }
    }
    swap(q[x].map[tx][ty],q[x].map[tx+go][ty]);
    down(x);
    flag=1;
    while(flag)
    {        
        clear_up(x);
        down(x);
    }
}
bool jz(int x)
{
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<7;j++)
        {
            w[q[x].map[i][j]]++;
        }
    }
    for(int i=1;i<=35;i++)
    {
        if(w[i]>0&&w[i]<3)
        return 1;
    }
    return 0;
}
void dfs(int x,int y,int step,int go)
{
    q[step].sp[0]=x;
    q[step].sp[1]=y;
    q[step].sp[2]=go;
    if(jz(step))return;
    if(cheak(step)&&step==n)
    {
        for(int i=1;i<=n;i++)
        {
            cout<<q[i].sp[0]<<" "<<q[i].sp[1]<<" "<<q[i].sp[2]<<'\n';
        }
        ans=1;
        return;
    }
    for(int i=0;i<7;i++)
    {
        for(int j=0;j<7;j++)
        {
            if(ans)return;
            if(q[step].map[i][j]==0)continue;    
            if(i+1<5)
            {
                if(ans)return;
                if(q[step].map[i][j]==q[step].map[i+1][j])continue;
                if(step+1>n)return;
                make_map(step+1,i,j,1);
                dfs(i,j,step+1,1);
            }
            if(i-1>=0)
            {
                if(ans)return;
                if(q[step].map[i][j]==q[step].map[i-1][j])continue;
                if(q[step].map[i-1][j]!=0)continue;
                if(step+1>n)return;
                make_map(step+1,i,j,-1);
                dfs(i,j,step+1,-1);
            }
        }        
    }

}
int main()
{
    cin>>n;
    for(int i=0;i<5;i++)
    {
        for(int j=0;j<=7;j++)
        {
            cin>>q[0].map[i][j];
            if(q[0].map[i][j]==0)
            break;
        }
    }
    dfs(0,0,0,0);
    if(!ans)
    cout<<-1;                    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章