DFS和BFS

BFS 廣度優先搜索

1. 空間是2^n,是指數級別的   空間很大

 2. 不會有爆棧的風險

3. 可以搜最短 、最小

BFS算法,即廣度優先搜索,它和深度優先搜索恰恰相反,它是一種適用於圖型結構的搜索,它和數據結構隊列緊密向量.
對於這種算法而言,它主要的步驟大致如下所示:
1. 找到當前可以拓展的點,將它放入候選隊列之中.
2. 每次選取候選隊列的隊頭,作爲當前狀態.

BFS性質
對於廣度優先搜索而言,它是一種逐層遍歷的算法,所有狀態按照入隊的先後順序具有層次單調性,也就是所謂的步數單調性質,如果每一次拓展恰好對應一部,那麼當第一個狀態第一次被訪問(入隊),就會得到從起始狀態到達該狀態的最少步數.

 dfs  深度優先搜索

1.空間和深度成正比的  空間很小

2.有爆棧的風險,比如樹深度最壞可能有100000層

3.不能搜最短、最小

4. 是一種適用於樹形結構的搜索,它和數據結構緊密相連。

   對於這種算法而言,它主要的步驟大致如下所示:
1). 找到當前可以拓展的點,那麼立即走入這個分支點.
2). 如果當前搜索分支,無效或者已經找到目標,那麼退回到上一步,即回溯.
3). 每一個點最多會被訪問兩次.訪問含義是入棧一次,出棧一次.

DFS性質
DFS序列就是深度優先搜索最爲重要的性質,它可以將一個子樹變成一個區間.

一般來說,dfs要好寫一些,因爲代碼短

例題     471. 棋盤3   (dfs)

有一個m×m的棋盤,棋盤上每一個格子可能是紅色、黃色或沒有任何顏色的。

你現在要從棋盤的最左上角走到棋盤的最右下角。 

任何一個時刻,你所站在的位置必須是有顏色的(不能是無色的),你只能向上、下、左、右四個方向前進。

當你從一個格子走向另一個格子時,如果兩個格子的顏色相同,那你不需要花費金幣;如果不同,則你需要花費1個金幣。 

另外,你可以花費2個金幣施展魔法讓下一個無色格子暫時變爲你指定的顏色。

但這個魔法不能連續使用,而且這個魔法的持續時間很短,也就是說,如果你使用了這個魔法,走到了這個暫時有顏色的格子上,你就不能繼續使用魔法;只有當你離開這個位置,走到一個本來就有顏色的格子上的時候,你才能繼續使用這個魔法,而當你離開了這個位置(施展魔法使得變爲有顏色的格子)時,這個格子恢復爲無色。 

現在你要從棋盤的最左上角,走到棋盤的最右下角,求花費的最少金幣是多少?

輸入格式

數據的第一行包含兩個正整數m,n,以一個空格分開,分別代表棋盤的大小,棋盤上有顏色的格子的數量。  

接下來的n行,每行三個正整數x,y,c,分別表示座標爲(x,y)的格子有顏色c,其中c=1代表黃色,c=0代表紅色。

相鄰兩個數之間用一個空格隔開。棋盤左上角的座標爲(1, 1),右下角的座標爲(m, m)。  

棋盤上其餘的格子都是無色,保證棋盤的左上角,也就是(1,1)一定是有顏色的。

輸出格式

輸出一行,一個整數,表示花費的金幣的最小值,如果無法到達,輸出-1。

數據範圍

1≤m≤100,
1≤n≤1000

輸入樣例:

5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0

輸出樣例:

8

輸入樣例#2:

5 5
1 1 0
1 2 0
2 2 1
3 3 1
5 5 0

輸出樣例#2:

-1

說明
輸入輸出樣例 1 說明

從(1,1)開始,走到(1,2)不花費金幣

從(1,2)向下走到(2,2)花費 1 枚金幣

從(2,2)施展魔法,將(2,3)變爲黃色,花費 2 枚金幣

從(2,2)走到(2,3)不花費金幣

從(2,3)走到(3,3)不花費金幣

從(3,3)走到(3,4)花費 1 枚金幣

從(3,4)走到(4,4)花費 1 枚金幣

從(4,4)施展魔法,將(4,5)變爲黃色,花費2 枚金幣,

從(4,4)走到(4,5)不花費金幣

從(4,5)走到(5,5)花費 1 枚金幣

共花費 8枚金幣。

輸入輸出樣例 2 說明

從(1,1)走到(1,2),不花費金幣

從(1,2)走到(2,2),花費1金幣

施展魔法將(2,3)變爲黃色,並從(2,2))走到(2,3))花費2 金幣

從(2,3)走到(3,3)不花費金幣

從(3,3)只能施展魔法到達(3,2),(2,3),(3,4),(4,3)
而從以上四點均無法到達(5,5),故無法到達終點,輸出−1
數據規模與約定
對於 30%的數據, 1≤m≤5,1≤n≤10。

對於 60%的數據, 1≤m≤20,1≤n≤200。

對於 100%的數據, 1≤m≤100,1≤n≤1,000。

解題報告
題意理解
這道題目的一句話題意是:

讓你從(1,1)走到(m,m),要求路上的花費代價最少.
如果你從A走到B,那麼走一步的代價有三種:
1. 若方格A的顏色等於方格B的顏色,那麼花費代價爲0
2. 若方格A的顏色不同於方格B的顏色,那麼花費代價爲1.
3. 若方格B的顏色爲無色,那麼相當於使用一次魔法,且花費的代價爲2,並且下一步不可以使用魔法.

判斷算法
一般來說,我們面對類似於迷宮這類,也就是具有非常明確的圖像顯示的題目,而言我們需要第一時間思考到這道題目是不是要用到搜索算法

算法選擇
搜索的算法有很多種.比如說深度優先搜索,廣度優先搜索,A搜索,IDA搜索,但是對於m≤100的數據範圍,而言我們並不需要多麼高級的搜索,只需要一點點記憶化剪枝+深度優先搜索即可

思路確定
首先對於一道搜索題目而言的話,我們有基本的三點目標

目標一:方向指示數組,這個數組在迷宮問題中,你可以認爲是走路方式,在動態規劃問題中,你可以認爲是拓展狀態的決策.

目標二:邊界處理,對於這道題目而言,邊界處理其實很簡單,就是在指定的區域裏面而已.也就是 (x,y)不越界

目標三:拓展準則,一道題目而言我們不僅僅要滿足邊界處理,還有更爲重要,也就是這道題目的最重要的一點,如何判斷我們走這一步是滿足條件的.以及這一步花費的代價

細節處理
目標三處理是我們這道題目的核心關鍵,也就是不同於其他問題的一個難點.下面我會詳細的說明.

上一次使用了魔法,那麼這一次不能使用魔法
上一次使用了魔法,判斷上一個點是不是和當前點顏色一致
顏色相同則不需要花費代價,否則花費1點代價
拓展的點是無顏色,那麼我們必須使用魔法,並且花費兩點代價

#include <bits/stdc++.h>
using namespace std;
const int N=1100;
const int dx[4]= {1,-1,0,0};
const int dy[4]= {0,0,1,-1};
int g[N][N],n,m,dis[N][N],vis[N][N],ans=1e9;
int check(int x,int y)
{
    if (x<1 || x>n || y<1 || y>n || vis[x][y])//在合法的矩陣內部,而且沒有訪問
        return true;//一個條件不滿足,則這一步無法拓展.
    return false;
}
void dfs(int x,int y,int nowc,int r)
{
    if(x==n && y==n && r<ans)
    {
        ans=r;
        return;
    }
    if(r>=dis[x][y])//如果發現當前的代碼,已經大於上一次我來到這個點的代價,那麼顯然可以剪枝,我再怎麼最優,也沒有上一次最優.
        return;
    dis[x][y]=r;//記錄最優值
    for(int i=0; i<4; i++)//四個拓展步驟
    {
        int xx=x+dx[i],yy=y+dy[i],cl=g[xx][yy];//拓展步驟,並且看下一步的顏色
        if (check(xx,yy))//如果不越界,而且沒有走過這個點的話
            continue;
        vis[xx][yy]=true;//已經訪問了
        if(nowc>=3 && cl!=0)//如果說上一次使用了魔法,但是我當前這個點不需要使用魔法的話
        {
            if((nowc+cl)%2==0)//這一步非常重要,大致意思是,這兩個點顏色相同,或者上一次魔法和本宮格顏色一樣.
                dfs(xx,yy,cl,r);//不需要花費代價
            else
                dfs(xx,yy,cl,r+1);//需要花費代價
        }
        else if(nowc==1 || nowc==2)//如果這個上一次沒有使用魔法的話
        {
            if(cl==1 || cl==2)//當前宮格不需要使用魔法
            {
                if(nowc!=cl)//顏色不一樣
                    dfs(xx,yy,cl,r+1);
                else//顏色一樣
                    dfs(xx,yy,cl,r);
            }
            else
                dfs(xx,yy,nowc+2,r+2);//使用魔法
        }
        vis[xx][yy]=false;//回溯處理.
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    memset(dis,0x3f,sizeof(dis));
    for(int i=1; i<=m; i++)
    {
        int x,y,c;
        cin>>x>>y>>c;
        g[x][y]=c+1;//習慣顏色處理.
    }
    vis[1][1]=true;//初始點已經訪問了
    dfs(1,1,g[1][1],0);//開始搜索
    if (ans==1e9)//找不到合法方案
        cout<<-1;
    else
        cout<<ans;//找到了
    return 0;
}

 

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