1. 程式人生 > 實用技巧 >[luogu P1312]Mayan遊戲

[luogu P1312]Mayan遊戲

其實就是一道鍛鍊碼力的簡單題……

看到題目中的\(0<x\leqslant 5\)也就知道是爆搜了吧(

我們仿照寫遊戲的方法多寫幾個函式,能夠有效降低錯誤率(確信

我們寫出大致的搜尋流程來:

如果當前步數大於\(n\)直接返回;
如果當前已經為終態,直接輸出答案;
否則列舉每種可能情況繼續搜。

沒錯就是這麼暴力

接下來講具體實現。

首先我們對全域性變數和陣列進行一下約定:

int n;//見題目
int board[10][10];//棋盤
int step[10][10];//儲存步數
int mem[10][10][10];//鑑於搜尋下一步的時候我們會不可避免地更改原陣列,我們需要儲存一下當前的狀態
bool del[10][10];//見remove函式部分

一開始的讀入非常簡單:讀到0換行即可

scanf("%d",&n);
for(int i=1;i<=5;i++)
{
    int cnt=0;
    while(1)
    {
        int xx;scanf("%d",&xx);
        if(xx==0)break;
        board[i][++cnt]=xx;
    }
}

我們需要對題目的一些操作進行一下模擬。

首先是fall函式:它讓懸空的塊下落。
可以發現,我們用一個變數來存下移的最終位置,就可以簡單地進行實現。

void fall()
{
    int cnt;
    for(int i=1;i<=5;i++)
    {
        cnt=0;
        for(int j=1;j<=7;j++)
        {
            if(board[i][j]==0)cnt++;
            else
            {
                if(cnt==0)continue;
                board[i][j-cnt]=board[i][j];
                board[i][j]=0;
            }
        }
    }
}

然後是三消remove函式:
我們分別暴力判斷橫向和縱向每一次可以消掉哪些塊,全都用del陣列給標記出來,最後一起消掉。
別忘了最後要將del陣列清零,消完之後fall一次讓可能懸空的塊下墜。
當然就像樣例一樣,一次消除是:消除當前->下墜->出現新的可三消塊->再次消除當前……
於是我們將這個函式修改一下,讓它發現本次有消除操作就返回1,沒有就返回0。
於是我們可以這樣呼叫remove函式:

while(remove());

簡單粗暴(

remove函式實現:

bool remove()
{
    bool flag=0;
    for(int i=1;i<=5;i++)
        for(int j=2;j<=6;j++)
            if(board[i][j]!=0&&board[i][j]==board[i][j-1]&&board[i][j]==board[i][j+1])
            {
                flag=1;
                del[i][j]=del[i][j-1]=del[i][j+1]=1;
            }
    for(int i=2;i<=4;i++)
        for(int j=1;j<=7;j++)
            if(board[i][j]!=0&&board[i][j]==board[i-1][j]&&board[i][j]==board[i+1][j])
            {
                flag=1;
                del[i][j]=del[i-1][j]=del[i+1][j]=1;
            }
    if(!flag)return 0;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            if(del[i][j])
                board[i][j]=del[i][j]=0;
    fall();
    return 1;
}

接下來是移動塊函式move和判斷是否結束函式gameover。
move函式只需要移動一次並呼叫fall墜落一次,然後remove即可。
gameover直接全屏掃。

void move(int xx,int yy,int dir)//用dir標記方向
{
    swap(board[xx][yy],board[xx+dir][yy]);
    fall();while(remove());
}
bool gameover()
{
    for(int i=1;i<=5;i++)
        if(board[i][1]!=0)
            return 0;
    return 1;
}

接下來是dfs函式的列舉部分。
首先我們用mem儲存一下;
然後只需要分方向列舉,列舉到一種情況就記錄步數繼續搜;
回溯的時候撤銷所記錄的步數,用mem陣列換回原來的狀態即可。
一個小剪枝:在交換之前判斷一下,避免將相同的塊交換即可。

for(int i=1;i<=5;i++)
    for(int j=1;j<=7;j++)
        mem[xx][i][j]=board[i][j];
for(int i=1;i<=5;i++)
    for(int j=1;j<=7;j++)
        if(board[i][j]!=0)
        {
            if(i<=4&&board[i][j]!=board[i+1][j])
            {
                move(i,j,1);
                step[xx][1]=i-1;step[xx][2]=j-1;step[xx][3]=1;
                dfs(xx+1);
                step[xx][1]=step[xx][2]=step[xx][3]=-1;
                for(int i=1;i<=5;i++)
                    for(int j=1;j<=7;j++)
                        board[i][j]=mem[xx][i][j];
            }
            if(i>=2&&board[i][j]!=board[i-1][j])
            {
                move(i,j,-1);
                step[xx][1]=i-1;step[xx][2]=j-1;step[xx][3]=-1;
                dfs(xx+1);
                step[xx][1]=step[xx][2]=step[xx][3]=-1;
                for(int i=1;i<=5;i++)
                    for(int j=1;j<=7;j++)
                        board[i][j]=mem[xx][i][j];
            }
        }

於是我們就這麼水完了一道藍題

完整程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n,board[10][10],step[10][10],mem[10][10][10];
bool del[10][10];
void fall()
{
    int cnt;
    for(int i=1;i<=5;i++)
    {
        cnt=0;
        for(int j=1;j<=7;j++)
        {
            if(board[i][j]==0)cnt++;
            else
            {
                if(cnt==0)continue;
                board[i][j-cnt]=board[i][j];
                board[i][j]=0;
            }
        }
    }
}
bool remove()
{
    bool flag=0;
    for(int i=1;i<=5;i++)
        for(int j=2;j<=6;j++)
            if(board[i][j]!=0&&board[i][j]==board[i][j-1]&&board[i][j]==board[i][j+1])
            {
                flag=1;
                del[i][j]=del[i][j-1]=del[i][j+1]=1;
            }
    for(int i=2;i<=4;i++)
        for(int j=1;j<=7;j++)
            if(board[i][j]!=0&&board[i][j]==board[i-1][j]&&board[i][j]==board[i+1][j])
            {
                flag=1;
                del[i][j]=del[i-1][j]=del[i+1][j]=1;
            }
    if(!flag)return 0;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            if(del[i][j])
                board[i][j]=del[i][j]=0;
    fall();
    return 1;
}
void move(int xx,int yy,int dir)
{
    swap(board[xx][yy],board[xx+dir][yy]);
    fall();while(remove());
}
bool gameover()
{
    for(int i=1;i<=5;i++)
        if(board[i][1]!=0)
            return 0;
    return 1;
}
void dfs(int xx)
{
    if(gameover())
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=3;j++)printf("%d ",step[i][j]);
            printf("\n");
        }
        exit(0);
    }
    if(xx>n)return;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            mem[xx][i][j]=board[i][j];
    for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
            if(board[i][j]!=0)
            {
                if(i<=4&&board[i][j]!=board[i+1][j])
                {
                    move(i,j,1);
                    step[xx][1]=i-1;step[xx][2]=j-1;step[xx][3]=1;
                    dfs(xx+1);
                    step[xx][1]=step[xx][2]=step[xx][3]=-1;
                    for(int i=1;i<=5;i++)
                        for(int j=1;j<=7;j++)
                            board[i][j]=mem[xx][i][j];
                }
                if(i>=2&&board[i][j]!=board[i-1][j])
                {
                    move(i,j,-1);
                    step[xx][1]=i-1;step[xx][2]=j-1;step[xx][3]=-1;
                    dfs(xx+1);
                    step[xx][1]=step[xx][2]=step[xx][3]=-1;
                    for(int i=1;i<=5;i++)
                        for(int j=1;j<=7;j++)
                            board[i][j]=mem[xx][i][j];
                }
            }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=5;i++)
    {
        int cnt=0;
        while(1)
        {
            int xx;scanf("%d",&xx);
            if(xx==0)break;
            board[i][++cnt]=xx;
        }
    }
    dfs(1);
    printf("-1\n");
    return 0;
}

最慢的點跑了1.09s,但還是夠AC了(笑