一道題學會廣/深度優先搜索(bfs/dfs):2020neu校賽熱身賽:找貓貓

Problem: 找貓貓
Time limit: 1s Mem limit: 64 MB AC/Submission: 32/169
Problem Description
貓貓和嘟嘟一起打遊戲, 貓貓被困在了M點不能移動,每一秒減少一個單位的HP, 需要隊友嘟嘟來救。但是現在嘟嘟不在貓貓旁邊,而是在遠離貓貓的另一個D點。當貓貓的HP變成負數之後,貓貓的人物就會死亡,而貓貓就會不高興。爲了讓貓貓能繼續玩下去, 嘟嘟需要馬上到M點去救貓貓(嘟嘟到達後可以讓貓貓的HP增加並且可以離開M點)。

現在給你這個n*m的遊戲地圖, D代表嘟嘟的位置,M代表貓貓的位置,*代表可以走的路,#代表不能翻越的牆。已知現在貓貓的HP爲t個單位,嘟嘟每1秒只能移動1個格子,只能走上下左右四個方向(不能斜着走,即四聯通) 。

現在問你,嘟嘟能不能在貓貓HP變爲負數之前到達M點? 如果能到達,請輸出貓貓最小損失的HP。不然,遊戲結束,貓貓很生氣,嘟嘟會跪機械鍵盤的,所以就要輸出一行字符串:“OMG! DUDU is bound to Kneel keyboard”(沒有引號)。

Input
第一行一個整數T,代表有T組數據。

每組數據的第一行有三個整數m, n, t,分別表示地圖的行數、地圖的列數和貓貓的HP。 (0<=m,n<=10 0<=t<=100)
接下來有m行數據,每行n個字符,代表一個mn的矩陣,矩陣有且僅有’D’ ’M’ ‘’ ‘#’字符組成(沒有引號)

Output
如果嘟嘟能在貓貓HP變爲負數之前到達M點,則輸出貓貓損失的最小HP。

否則輸出“OMG! DUDU is bound to Kneel keyboard”(沒有引號)

Sample Input
2
3 3 10
D**
#
#M*
3 3 2
D**
#
#M*
Sample Output
5
OMG! DUDU is bound to Kneel keyboard
解1:bfs廣搜
分析:
廣搜的核心是在每一個當前的起點找到可以擴展的所有點,
再把擴展出的所有點作爲新的起點,繼續擴展,擴展1次則step+1,
類比等高線,可以把廣搜看作找出圖的等step線,然後判斷終點在哪條等step線上
因此,只要擴展出的是目的地,此時記錄的step一定是最少的


```cpp
#include<bits/stdc++.h>//萬能頭文件
using namespace std;
struct note
{
    int x;
    int y;//座標
    int f;//本題不需要求路徑,可忽略
    int s;//步數
}que[144];//隊列數組
int a[12][12];//記錄圖
const int dx[4]={1,-1,0,0};//四個方向移動
const int dy[4]={0,0,1,-1};
int main()
{
    iostream::sync_with_stdio(0);//關同步,加快輸入速度

    int T;
    cin>>T;
    while(T--)
    {
        memset(que,0,sizeof(que[0])*144);//初始化
        memset(a,0,sizeof(a[0][0])*144);
        int m,n,HP;
        cin>>m>>n>>HP;
        int i,j,k,t;
        int sx,sy,ex,ey;//開始,結束點
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                /*先轉換圖,走過的路四周圍牆和障礙物用0代替,
                 可走的路用1代替,可省去一個book數組*/
                char c;
                cin>>c;
                if(c=='*')
                {
                    a[i][j]=1;
                }
               else if(c=='D')
                {
                    sx=i;
                    sy=j;
                }
               else if(c=='M')
                {
                    ex=i;
                    ey=j;
                    a[i][j]=1;
                }
            }
        }
        int tx=sx,ty=sy,head=1,tail=2,flag=0;//tx,ty是當前的座標,head是起始點,tail是擴展出的點,flag記錄是否到終點
        que[head].x=sx;//對頭初始化
        que[head].y=sy;
        while(head<tail)//隊列不爲空
        {
            for(i=0;i<4;i++)//從當前點開始遍歷四個方向
            {
                tx=que[head].x+dx[i];//head點可向4個方向走,成爲當前駐點
                ty=que[head].y+dy[i];
              if(a[tx][ty])//駐點如果不是走過的點、四周圍牆和障礙物
              {
                a[tx][ty]=0;//標記爲走過的點
                que[tail].x=tx;//駐點入隊
                que[tail].y=ty;
                que[tail].f=head;//路徑
                que[tail].s=que[head].s+1;
                tail++;//記錄下一個點
              }
              if(ex==tx&&ey==ty)
                {
                flag=1;//到終點了
                break;
                }
            }
             if(flag==1)
                break;
            head++;//這裏非常重要,在一個起點擴展完4個點後,這4個點又作爲新起點再擴展新的點
        }
        if(head==tail||HP<que[tail-1].s)//如果嘟嘟四周都是圍牆呢?因此要判斷head?=tail
        cout<<"OMG! DUDU is bound to Kneel keyboard"<<endl;
        else
            cout<<que[tail-1].s<<endl;

    }
    return 0;
}

解2:深搜dfs
分析:深搜的核心是分解成當下怎麼做和下一步怎麼做,當下有4個可能延展方向,選一個方向作爲新的當下。重複此過程。

#include<bits/stdc++.h>
using namespace std;
int m,n,ex,ey,step,ans;//行列,終點,當前路徑步數,最小步數
bool a[13][13];//記錄地圖
const int X[4]={1,-1,0,0};//四個方向
const int Y[4]={0,0,1,-1};
void dfs(int sx,int sy,int step)//當前起點與該路徑步數
{
    int tx,ty;//當前擴展出的座標
    if(step>=ans)//剪枝,如果已經比當前的最短路徑長了,就沒必要再搜了
        return;
    if(ex==sx&&ey==sy)
    {
        ans=step;//記錄到達終點更短的步數
        return;
    }
    for(int i=0;i<4;i++)//遍歷四個方向
    {
        tx=sx+X[i];//向四個方向中的一個方向擴展
        ty=sy+Y[i];
        if(a[tx][ty])//如果不是障礙物,邊緣或已走過的路
        {
            a[tx][ty]=0;//記錄已經走過
            dfs(tx,ty,step+1);//該點作爲起點,向下擴展一步
            a[tx][ty]=1;//執行到這步說明當前步作爲起點的探索失敗了,但其他搜索路徑可能會經過該點,因此要還原
        }
    }
    return;
}
int main()//主函數同理
{
    iostream::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--)
    {
        ans=999999;
        memset(a,false,sizeof(a));
    int HP,sx,sy;
    cin>>m>>n>>HP;
    int i,j,k;
    for(i=1;i<=m;i++)
    {
        for(j=1;j<=n;j++)
        {
            char c;
            cin>>c;
            if(c=='*')
            {
                a[i][j]=1;
            }
            else if(c=='D')
            {
                sx=i;
                sy=j;
            }
            else if(c=='M')
            {
                ex=i;
                ey=j;
                a[i][j]=1;
            }
        }
    }
    dfs(sx,sy,0);
     if(HP<ans)
       cout<<"OMG! DUDU is bound to Kneel keyboard"<<endl;
    else
        cout<<ans<<endl;
    }

    return 0;
}

對比兩種算法:bfs更快,因爲它不用返回到前一步,第一次到達即最短路徑,但是可能會更佔內存;dfs慢,但可以記錄所有路徑,可以解決需要枚舉所有排列的問題,而且內存可能佔的更少

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章