1. 程式人生 > >『BFS與DFS:搜尋專題』

『BFS與DFS:搜尋專題』

<前言>
暴力搜尋不需要解釋


<更新提示>

<第一次更新>


<正文>

DFS

dfs,全名depth-first-search,即深度優先搜尋。如其名,其最大的特點就是以深度為優先條件對搜尋樹的節點進行拓展,以棧來維護其搜尋次序。當然,多數dfs用系統棧實現——遞迴。
概念不用多說,以例題和程式碼實現為重。

例題
1

8皇后問題
題目描述
在一個n×n的棋盤上放置n個國際象棋中的皇后,要求所有的皇后之間都不形成攻擊。請你給出所有可能的排布方案數。
輸入格式
一個整數n
輸出格式
一個整數表示方案數
樣例資料
input
4
output
2
資料規模與約定
n<=8
時間限制:
1s
1s
空間限制:
256MB

解析

經典dfs搜尋與回溯的入門題,其搜尋策略如下:
按行放置每一個皇后,並使用三個陣列標記同列,兩條對角線是否有其他的皇后存在,如果存在,則為不合法的放置,如果合法,繼續遞迴,嘗試放置下一個皇后。直至放置第n個皇后,方案數增加。呼叫遞迴時注意標記數字,遞迴結束記得回溯:清除陣列標記。

程式碼實現如下:

#include<bits/stdc++.h>
using namespace std;
int n,tot=0;
bool line[1000]={},ri[1000]={},le[1000]={};
void solve(int row)
{
    if(row==n)
    {
        tot++;
        return
; } for(int i=0;i<n;i++) { if(!(line[i]||ri[row-i+n]||le[row+i])) { line[i]=ri[row-i+n]=le[row+i]=true; solve(row+1); line[i]=ri[row-i+n]=le[row+i]=false; } } } int main() { cin>>n; solve(0); cout<<tot<<endl; return
0; }
2

工作分配問題
題目描述
設有n件工作分配給n個人。將工作i 分配給第j 個人所需的費用為Cij。試設計一個演算法,為每一個人都分配1 件不同的工作,並使總費用達到最小。 設計一個演算法,對於給定的工作費用,計算最佳工作分配方案,使總費用達到最小。
輸入格式
第一行有1 個正整數n (1≤n≤20)。
接下來的n行,每行n個數,表示工作費用。
輸出格式
一個整數,表示最小總費用
樣例資料
input
3
10 2 3
2 3 4
3 4 5
output
9
資料規模與約定
時間限制:
1s
1s
空間限制:
256MB

解析

鑑於資料量極小,考慮直接暴力搜尋即可,其策略如下:利用遞迴搜尋每一種情況。遞迴的形參為:深度(人數),花費。需要一個數組來標記某個人是否已經工作,避免重複,當搜尋的人數大於n時,檢視能否更新答案。其剪枝優化如下:當形參花費已經大於之前得出的答案時,返回遞迴。
程式碼實現如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=999999999;
int Min=INF,spend[1080][1080],vis[1080]={},n;
void dfs(int depth,int sum)
{
    if(sum>Min)return;
    if(depth>n)
    {
        Min=min(Min,sum);
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            dfs(depth+1,sum+spend[depth][i]);
            vis[i]=0;
        }
    }

}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>spend[i][j];
        }
    }
    dfs(1,0);
    cout<<Min<<endl;
}
3

最佳排程問題
題目描述
假設有n 個任務由k 個可並行工作的機器完成。
完成任務i 需要的時間為ti。
試設計一個演算法找出完成這n 個任務的最佳排程,使得完成全部任務的時間最早。 一旦任務i由某臺機器完成,中途不能更換機器。
程式設計任務:
對任意給定的整數n 和k,以及完成任務i 需要的時間為ti,i=1~n 。程式設計計算完成這n個任務的最佳排程
輸入格式
第一行有2 個正整數n 和k。
第2 行的n 個正整數是完成n 個任務需要的時間。
輸出格式
完成全部任務的最早時間。
樣例資料
input
7 3
2 14 4 16 6 5 3
output
17
input
5 2
8 9 3 7 7
output
17
資料規模與約定
保證
n<=20,k<=10
n<=20,k<=10

時間限制:
1s
1s
空間限制:
256MB

解析

依然使用深搜列舉每一種情況。遞迴的形參設定也和上一題相同:深度(已經分配的任務數),已經花費的時間。我們建立Time陣列,Time[i]表示第I臺機器工作的時間,總花費時間即max{Time[i]}(i=1~n),利用其方便深搜時的統計。對於本題,資料量較大,暴力搜尋必然超時。我們需要兩個剪枝和一個優化:
1)剪枝:在遞迴函式頂剪枝,如果答案已經比之前得出的某個答案大,返回。
2)剪枝:在遞迴時剪枝,如果機器i以前花費的時間加上這次遞迴呼叫需要花費的時間已經1大於之前得出的某個答案,跳過本次遞迴。(與剪枝1並不重複!!)
3)優化:將機器花費從大到小排序,更好的響應剪枝1。

程式碼實現如下:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define begin {
#define end } 
#define mset(name,num) memset(naem,num,sizeof(num))
using namespace std;
inline void read(int &k)
begin
    int x=0,w=0;char ch;
    while(!isdigit(ch))begin w|=ch=='-';ch=getchar(); end
    while(isdigit(ch))begin x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); end
    k=(w?-x:x);return;
end
int n,k,Spend[800]={},Time[800]={},Ans=0x3f3f3f3f;
inline bool cmp(int a,int b)begin return a>b; end 
inline void input()
begin
    read(n);read(k);
    for(int i=1;i<=n;i++)read(Spend[i]);
end
inline void Search(int depth,int Usedtime)
begin
    if(depth>n)
    begin
        Ans=min(Ans,Usedtime);
        return;
    end 
    if(Usedtime>=Ans)return;
    for(int i=1;i<=k;i++)
    begin
        if(Time[i]+Spend[depth]<Ans)
        begin
            Time[i]+=Spend[depth];
            Search(depth+1,max(Usedtime,Time[i]));
            Time[i]-=Spend[depth];
        end
    end
end
int main()
begin
    input();
    sort(Spend+1,Spend+n+1,cmp);
    Search(1,0);
    cout<<Ans<<endl;
    return 0; 
end

(請忽略清奇的碼風)

4

因式分解
題目描述
將大於1的自然數n進行因式分解,滿足           
n=a1∗a2∗a3∗..am
編一程式,對任意的自然數
n(1 < n ≤ 2,000,000,000)
n(1 < n ≤ 2,000,000,000)
,求 n的所有形式不同的因式分解 方案總數。
如 n=12,共有8種分解方案,他們分別是:
   12=12
   12=6×2
   12=4×3
   12=3×4
   12=3×2×2
   12=2×6
   12=2×3×2
   12=2×2×3
輸入格式
一個整數n
輸出格式
一個整數m,代表不同的因式分解的方案總數。
樣例資料
input
12
output
8
資料規模與約定
時間限制:
1s
空間限制:
256MB

解析

搜尋技巧:預處理。
我們預處理出n的所有因數,並記錄在f數組裡,並將其從小到大排序。其搜尋策略如下:利用因子表列舉n的每一個因子,並遞迴地利用n的因子表繼續列舉它們的因子,直至列舉至1的因子,返回並將方案數增加即可。
程式碼實現如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define begin {
#define end } 
#define mset(name,num) memset(naem,num,sizeof(num)
using namespace std;
inline void read(int &k)
begin
    int x=0,w=0;char ch;
    while(!isdigit(ch))begin w|=ch=='-';ch=getchar(); end
    while(isdigit(ch))begin x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); end
    k=(w?-x:x);return;
end
int ans=0,n,f[100080]={},t=0; 
void dfs(int k)
{
    if(k==1)begin ans++;return; end
    for(int i=1;i<=t&&f[i]<=k;i++)if(k%f[i]==0)dfs(k/f[i]);
}
int main()
begin
    cin>>n; 
    for(int i=2;i*i<=n;i++)
    begin
        if(n%i==0)
        begin
            f[++t]=i;
            if(i*i!=n)f[++t]=n/i;
        end 
    end
    f[++t]=n;
    sort(f+1,f+t+1);
    dfs(n);
    cout<<ans;
end
小結

dfs一般沒有固定的模板,需要我們根據題目建立模型,制定搜尋策略,特別是遞迴函式形參的設定,需要深刻考慮。在遇到解題策略不明的其他題目時,也可以考慮暴力搜尋答案得分。dfs的基本技巧與剪枝優化如下:
1)可行性剪枝:檢查目前的答案是否符合要求或題目限制。
2)最優化剪枝:檢查答案是否已經比之前得出的答案更差。
3)預處理:簡化搜尋程式碼量與時間複雜度
4)記憶化:空間換時間,記錄已經搜尋過的狀態。
5)數學推導:從而得出1)或2)的剪枝優化。

dfs搜尋時的策略一定要明確,先思考,再敲程式碼,千萬不能盲目下手,只會浪費時間。

BFS

bfs,全名breath-first-search,即廣度優先搜尋。如其名,其最大的特點就是以層數為優先條件對搜尋數的節點進行逐層拓展,以佇列來維護其搜尋次序。bfs用stl自帶佇列或手寫佇列實現。
bfs概念與實現入門見福州集訓Day2,本篇以例題講解為重。

例題
1

奇怪的電梯
題目描述
有一天我做了一個夢,夢見了一種很奇怪的電梯。大樓的每一層樓都可以停電梯,而且第i層樓(1<=i<=N)上有一個數字Ki(0<=Ki<=N)。電梯只有四個按鈕:開,關,上,下。上下的層數等於當前樓層上的那個數字。當然,如果不能滿足要求,相應的按鈕就會失靈。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),從一樓開始。在一樓,按“上”可以到4樓,按“下”是不起作用的,因為沒有-2樓。那麼,從A樓到B樓至少要按幾次按鈕呢?
輸入格式
輸入檔案共有二行,第一行為三個用空格隔開的正整數,表示N,A,B(1≤N≤200, 1≤A,B≤N),第二行為N個用空格隔開的正整數,表示Ki。
輸出格式
輸出檔案僅一行,即最少按鍵次數,若無法到達,則輸出-1。
樣例資料
input
5 1 5
3 3 1 2 5
output
3
資料規模與約定
時間限制:
1s
空間限制:
256MB

分析

bfs入門題,搜尋策略清晰,直接依據題意,構建距離陣列dis,標記陣列vis,利用while迴圈bfs搜尋即可,手寫佇列維護。注意,本題的細節為:當a=b時,直接輸出0。
程式碼實現如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define begin {
#define end } 
#define mset(name,num) memset(naem,num,sizeof(num))
using namespace std;
inline void read(int &k)
begin
    int x=0,w=0;char ch;
    while(!isdigit(ch))begin w|=ch=='-';ch=getchar(); end
    while(isdigit(ch))begin x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); end
    k=(w?-x:x);return;
end
int n,a,b,move[280]={},head=1,tail=0,vis[280]={},dis[280]={},q[280]={};
int main()
begin
    read(n);read(a);read(b);
    for(int i=1;i<=n;i++)read(move[i]);
    vis[a]=1,dis[1]=0;
    q[++tail]=a;
    while(head<=tail)
    begin
        int temp=q[head++];
        if(temp==b)break;
        if(temp+move[temp]<=n&&vis[temp+move[temp]]==0)
        begin
            vis[temp+move[temp]]=1;
            dis[temp+move[temp]]=dis[temp]+1;
            q[++tail]=temp+move[temp];
        end
        if(temp-move[temp]>=1&&vis[temp-move[temp]]==0)
        begin
            vis[temp-move[temp]]=1;
            dis[temp-move[temp]]=dis[temp]+1;
            q[++tail]=temp-move[temp]; 
        end
    end
    if(a==b)begin cout<<0<<endl; return 0; end
    else
    begin 
        if(dis[b]==0)cout<<-1<<endl;
        else cout<<dis[b]<<endl;
        return 0;
    end
end
2

青銅蓮花池
題目描述
Farmer John為了滿足寧智賢對美的享受而安裝了人工湖。矩形的人工湖分成M行N列(1 <= M <= 30; 1 <= N <= 30)的方形小格子。有些格子有美麗的荷葉,有些有岩石,剩下的格子有的只是美麗的藍色湖水。 寧智賢通過從一片荷葉跳到另一片荷葉上來練習芭蕾。它現在正站在一片荷葉上(看輸入資料瞭解具體位置)。它希望通過在荷葉上跳躍來到達另一片荷葉。它既不能跳到水裡也不能跳到岩石上。 只有新手才會感到吃驚:寧智賢的跳躍有點類似國際象棋中馬那樣的移動,在一個方向上(如水平方向)移動M1(1 <= M1 <= 30)“格”,然後再在另一個方向上(如豎直方向)移動M2 (1 <= M2 <= 30; M1 != M2)格,所以寧智賢有時可能有多達8中的跳躍選擇。
給出池塘的構造以及寧智賢跳躍的形式,找出寧智賢從一個位置移動到另一個位置所需的最小的跳躍次數。這個跳躍對於所給的測試資料總是可能的。
輸入格式
第 1 行: 四個空格分開的整數: M, N, M1, 和 M2
第 2 至 M+1行: 第i+1行用N個空格分開的整數描述池塘第i行,0表示水,1表示荷葉,2表示岩石,3表示寧智賢現在站的那塊荷葉,4表示跳躍的終點。
輸出格式
一個整數,是寧智賢從一塊荷葉跳到另一塊荷葉所需的最小的跳躍數。
樣例資料
input
4 5 1 2
1 0 1 0 1
3 0 2 0 4
0 1 2 0 0
0 0 0 1 0

輸入說明:

寧智賢在第二行的左邊開始;她的目的地在第二行的右邊。池塘中有幾塊荷葉和岩石。
output
2

輸出說明:

機智的寧智賢跳到了(1,3)的荷葉上,再跳到目的地。
資料規模與約定
保證
1≤M≤30,1≤N≤30
1≤M≤30,1≤N≤30
時間限制:
1s
空間限制:
256MB

分析

看到最短距離,又沒有邊權,不是最短路,顯然想到具有最短距離搜尋性質的bfs。本題為迷宮類bfs,可以構造方位陣列減輕程式碼量。其搜尋策略明確:選擇所有可拓展的位置拓展即可。
程式碼實現如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,m1,m2;
int Map[5000][5000]={},dx[10],dy[10],visited[5000][5000],road[5000][5000]={};
int beginx,beginy,endx,endy,head=0,tail=0;;
struct QUEUE
{
    int x,y;
}que[100080]={};
inline int read()  
{  
    int X=0,w=0; char ch=0;  
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}  
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();  
    return w?-X:X;  
}
inline void input()
{
    m=read();n=read();
    m1=read();m2=read();
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            Map[i][j]=read();
        }
    }
}  
inline void build_Map()
{
    dx[0]=dx[1]=dy[4]=dy[6]=m1;
    dx[2]=dx[3]=dy[5]=dy[7]=-m1;
    dx[4]=dx[5]=dy[0]=dy[2]=m2;
    dx[6]=dx[7]=dy[1]=dy[3]=-m2;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(Map[i][j]==2)Map[i][j]=0;
            if(Map[i][j]==3){beginx=i;beginy=j;}
            if(Map[i][j]==4){endx=i;endy=j;}
        }
    }
}
inline void bfs()
{
    que[++tail].x=beginx;que[tail].y=beginy;
    visited[beginx][beginy]=1;
    while(head++<tail)
    {
        int x=que[head].x,y=que[head].y;
        for(int i=0;i<8;i++)
        {
             if(!visited[x+dx[i]][y+dy[i]]&&Map[x+dx[i]][y+dy[i]])
            {
                road[x+dx[i]][y+dy[i]]=road[x][y]+1;
                if(x+dx[i]==endx&&y+dy[i]==endy) return;
                que[++tail].x=x+dx[i];que[tail].y=y+dy[i];
                visited[x+dx[i]][y+dy[i]]=1;
            }
        }
    }
}
inline void output()
{
    cout<<road[endx][endy]<<endl;
}
int main()
{
    input();
    build_Map();
    bfs();
    output();
    return 0;
}
3

顯示影象
題目描述
古老的顯示屏是由N×M個象素(Pixel)點組成的。一個象素點的位置是根據所在行數和列數決定的。例如P(2,1)表示第2行第1列的象素點。那時候,螢幕只能顯示黑與白兩種顏色,人們用二進位制0和1來表示。0表示黑色,1表示白色。當計算機發出一個指令:P(x,y)=1,則螢幕上的第x行第y列的陰極射線管就開始工作,使該象素點顯示白色,若P(x,y)=0,則對應位置的陰極射線管不工作,象素點保持黑色。在某一單位時刻,計算機以N×M二維01矩陣的方式發出顯示整個螢幕影象的命令。 例如,螢幕是由3×4象素點組成,在某單位時刻,計算機發出如下命令:
0001
0011
0110
則螢幕影象為:
這裡寫圖片描述

(假設放大後,一個格子表示一個象素點)
由於未知的原因,顯示黑色的象素點總是受顯示白色的象素點的影響——可能是陰極射線管工作的作用。並且,距離越近,影響越大。這裡的距離定義如下:設有象素點P1(x1,y1)和象素點P2(x2,y2),則它們之間的距離D(P1,P2):D(P1,P2)=|x1-x2|+|y1-y2| 在某一時刻,計算機發出顯示命令後,科學家們期望知道,每個象素點和其最近的顯示白色的象素點之間的最短距離是多少——科學家們保證螢幕上至少有一個顯示白色的象素點。 上面的例子中,象素P(1,1)與最近的白色象素點之間的距離為3,而象素P(3,2)本身顯示白色,所以最短距離為0。
輸入格式
第一行有兩個數字,N和M (1<=N,M<=1000),表示螢幕的規格。
以下N行,每行M個數字,0或1。為計算機發出的顯示命令。
輸出格式
輸出檔案有N行,每行M個數字,中間用1個空格分開。第i行第j列的數字表示距象素點P(i,j)最近的白色象素點的最短距離。
樣例資料
input
3 4
0001
0011
0110
output
3 2 1 0
2 1 0 0
1 0 0 1
資料規模與約定
時間限制:
1s
1s
空間限制:
256MB
註釋
【資料範圍】
對於30%的資料:
N×M≤10000
N×M≤10000

對於100%的資料:
N×M≤
1000
2
N×M≤1000*1000

分析

這道題的特殊性在於搜尋的多源點,但鑑於最短曼哈頓距離性質,還是bfs。對於多源點,只需在輸入時將所有黑色畫素點直接加入佇列,同時執行多顆搜尋樹的搜尋,並使用同一個佇列維護即可。由於這有違常規的bfs,可以想象為每一棵搜尋樹的根節點又同時都連結了一個共同的不存在的-1層節點,即總的根節點,每一棵搜尋樹都是總搜尋樹的子樹。所以bfs仍然正常搜尋不誤。
程式碼實現如下:

#include<bits/stdc++.h>
using namespace std;
int n,m,tail=0,head=1;
struct MAP
{
    int x,y;
}q[100080]={};
bool vis[10800][10800];
int dis[10800][10800]={};
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
int main()
{
    memset(vis,true,sizeof(vis));
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        string line;
        cin>>line;
        for(int j=0;j<line.size();j++)
        {
            if(line[j]=='0')vis[i][j+1]=false;
            else dis[i][j+1]=0,vis[i][j+1]=true,q[++tail].x=i,q[tail].y=j+1;
        }
    }
    for(head=1;head<=tail;head++)
    {
        for(int i=0;i<4;i++)
        {
            int xx=q[head].x+dx[i],yy=q[head].y+dy[i];
            if(!vis[xx][yy])
            {
                dis[xx][yy]=dis[q[head].x][q[head].y]+1;
                vis[xx][yy]=1;
                q[++tail].x=xx;
                q[tail].y=yy;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<m;j++)
        {
            cout<<dis[i][j]<<" ";
        }
        cout<<dis[i][m]<<endl;
    }
    return 0;
}
4

3.奇怪的機器人
(robot.pas/c/cpp)
【問題描述】
有一個奇怪的機器人,它被關在一個實驗室裡。
實驗室是一個長方形,被分成了n行m列個格子,某些格子能走而有些不能
現在這個機器人每次能走到與他相鄰的上下左右四個格子(如果相鄰的格子能走的話),但是不同方向的花費不一樣,往上,下,左,右四個方向走一次分別需要花費1,2,3,4塊錢。
機器人在第x1行y1列的格子上,出口在x2行y2列的格子上,問你機器人想出去最少需要花多少錢?
【輸入】
第一行兩個用空格隔開的整數n和m
接下來n行,每行m個字元,表示地圖
如果第i行j列個字元為’.’,表示這個地方能走,否則不能
最後一行四個用空格隔開的整數x1,y1,x2,y2
保證第x1行y1列和第x2行y2列一定是’.’
【輸出】
一行一個整數,如果機器人能出去則為它最少需要花多少錢,否則為-1
【輸入輸出樣例1】
4 4
….
.**.
..*.
….
3 2 3 4 11

【輸入輸出樣例2】
1 3
.*.
1 1 1 3 -1

【資料範圍】
1<=n,m<=50

解析

這道題的實質還是bfs,這道題的優秀之處在於他對於不同方向的移動有不同的花費,需要深入思考。引入了邊權的引數後,迷宮類問題的bfs模板看似不行,實則還是這樣。其搜尋策略如下:對於邊權的存在,繞路反而花費更小的可能也隨之出現。我們放棄vis陣列,對於一個節點,我們可以訪問多遍,前提在於花費更少,這就類似於三角形鬆弛迭代,bfs就更像圖論演算法中的spfa。但程式碼仍然和bfs模板很像,實現難度很小。
程式碼實現如下:

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
    int w=0,x=0;char ch;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    k=(w?-x:x);return;
}
int n,m,Map[80][80]={},stx,sty,enx,eny,spend[80][80],Max;
int head=1,tail=0;struct QUEUE{int x,y;}q[80*80]={};
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};
int main()
{
    //freopen("robot.in","r",stdin);
    //freopen("robot.out","w",stdout);
    read(n),read(m);
    memset(spend,10,sizeof(spend));
    Max=spend[1][1];
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            char temp;
            cin>>temp;
            if(temp=='*')Map[i][j]=0;
            else Map[i][j]=1;
        }
    } 
    read(stx),read(sty);
    read(enx),read(eny);
    spend[stx][sty]=0;
    q[++tail]=(QUEUE){stx,sty};
    while(head<=tail)
    {
        int tx=q[head].x,ty=q[head].y;
        head++;
        for(int i=1;i<=4;i++)
        {
            int px=tx+dx[i],py=ty+dy[i];
            if(px<1||py<1||px>n||py>m)continue;
            if(spend[tx][ty]+i<spend[px][py]&&Map[px][py])
            {
                spend[px][py]=spend[tx][ty]+i;
                q[++tail]=(QUEUE){px,py};
            }
        }
    }
    if(spend[enx][eny]!=Max)cout<<spend[enx][eny]<<endl;
    else cout<<-1<<endl;
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}
小結

bfs與dfs相比,就更規範了。每一道bfs題基本都是模板的運用或更改。但其仍有許多靈活之處,例如向圖論演算法的拓展,搜尋路徑的輸出等,需要我們靈活掌握。bfs的優化相對更少一些,但也不代表沒有。bfs的著重點在於對題目的分析,確認其是否應該使用bfs,需要怎樣優化和更改。bfs的難題也有許多,例如磁帶儲存器(會專放一篇部落格講)等題,需要多加練習。

關於搜尋專題的小結和考試的總結

關於搜尋專題,基本上就結束了,對於A*,IDA*,迭代加深等更高階的搜尋演算法就留給以後再講吧。NOIP普及組的搜尋就以bfs和dfs為主,小結也寫在上文了,希望NOIP能派上用場。這次考試是我暑假繼集訓考的最差的一次,只考了280分(滿分400),主要問題在於過於執著寫出正解和沒能靈活運用程式碼技巧減輕程式碼量,真正考試時,任何能得分的方法都是好方法,暴力,打表,都是可以嘗試的方法,不要侷限於好好敲題,考試的時候,就某題算得不了滿分,那你的目標就是盡你所能得到最高的分數,這就是搜尋的信仰。


<後記>


<廢話>