BFS算法详解
一、概述
作為搜索算法的一種,DFS對于尋找一個解的NP(包括NPC)問題作用很大。但是,搜索算法畢竟是時間復雜度是O(n!)的階乘級算法,它的效率非常低,在數據規模變大時,這種算法就顯得力不從心了。當節點v的所有邊都己被探尋過,搜索將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已發現從源節點可達的所有節點為止。如果還存在未被發現的節點,則選擇其中一個作為源節點并重復以上過程,整個進程反復進行直到所有節點都被訪問為止。屬于盲目搜索。這些是百度上面的一些話,根據自己理解,就是說這個算法運用的時候就是找一個頭結點,然后沿著這個頭結點一直找下去,直到走到最后一個滿足條件的分節點,然后再尋找另一條路徑,當沿著一條路走不滿足條件時會自動的跳入上一層節點進行判斷。dfs算法通常與回溯算法一起使用,下面的例子中將會提到這些問題。每次做這種類型的題可以先畫出dfs遍歷的路徑圖,這樣有利于寫程序時的合理思維二、實例講解
2.1、例題1
Description Michael喜歡滑雪百這并不奇怪, 因為滑雪的確很刺激。可是為了獲得速度,滑的區域必須向下傾斜,而且當你滑到坡底,你不得不再次走上坡或者等待升降機來載你。Michael想知道載一個區域中最長底滑坡。區域由一個二維數組給出。數組的每個數字代表點的高度。下面是一個例子 1 2 3 4 516 17 18 19 615 24 25 20 714 23 22 21 813 12 11 10 9一個人可以從某個點滑向上下左右相鄰四個點之一,當且僅當高度減小。在上面的例子中,一條可滑行的滑坡為24-17-16-1。當然25-24-23-...-3-2-1更長。事實上,這是最長的一條。 Input 輸入的第一行表示區域的行數R和列數C(1 <= R,C <= 100)。下面是R行,每行有C個整數,代表高度h,0<=h<=10000。 Output 輸出最長區域的長度。 Sample Input 5 5 1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9 Sample Output 25 #include<iostream> #include<cstdio> using namespace std; int R, C; int map[105][105]; int mark[105][105] = { 0 }; int dfs(int i, int j) { int k; if (mark[i][j]) return mark[i][j]; if (i != 0 && map[i - 1][j] < map[i][j]) { k = dfs(i - 1, j) + 1; if (k> mark[i][j]) mark[i][j] = k; } if (i != R - 1 && map[i + 1][j] < map[i][j]) { k = dfs(i + 1, j) + 1; if (k>mark[i][j]) mark[i][j] = k; } if (j != 0 && map[i][j - 1]<map[i][j]) { k = dfs(i, j - 1) + 1; if (k>mark[i][j]) mark[i][j] = k; } if (j != C - 1 && map[i][j + 1]<map[i][j]) { k = dfs(i, j + 1) + 1; if (k>mark[i][j]) mark[i][j] = k; } return mark[i][j]; } int main() { int i, j, k, sum = 0; scanf("%d%d", &R, &C); for (i = 0; i < R; i++) { for (j = 0; j < C; j++) { scanf("%d", &map[i][j]); } } for (i = 0; i < R; i++) { for (j = 0; j < C; j++) { k = dfs(i, j); if (k>sum) sum = k; } } cout << sum + 1 << endl; return 0; }**解析:**這個題就是把每個數從四個方向都遍歷一次,如果滿足遞減的話就接著dfs,不滿足時候把這個數存起來
這個題有幾個注意的問題,就是第一個要考慮好邊緣臨界點,就是四周的點不可以進行某些方向的移動,其次還有一點特別要注意,dfs中的if (mark[i][j]) return mark[i][j];這句話就是為了重復計算,假如從24開始的話已經算出來23,然后如果從25開始,遇到24的話直接可以找到23,而不用在遍歷一次,節省了時間。
2.2、實例2
八皇后問題是一個以國際象棋為背景的問題:如何能夠在 8×8 的國際象棋棋盤上放置八個皇后,使得任何一個皇后都無法直接吃掉其他的皇后?為了達到此目的,任兩個皇后都不能處于同一條橫行、縱行或斜線上。八皇后問題可以推廣為更一般的n皇后擺放問題:這時棋盤的大小變為n×n,而皇后個數也變成n。當且僅當 n = 1 或 n ≥ 4 時問題有解。Input 無輸入。Output 按給定順序和格式輸出所有八皇后問題的解(見Sample Output)。Sample InputSample OutputNo. 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 No. 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 No. 3 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 No. 4 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 No. 5 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 No. 6 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 No. 7 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 No. 8 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 No. 9 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 ...以下省略**解析:**這個就是定義hang 【num】和列i,然后每一行從第一個位置開始放皇后,如果可以的話就標記為1,然后接著往下找第二行,也是從第二行第一個位置開始找,滿足接著第三行。當如果發現第三行不能再放置皇后了,這個時候第三行那個位置已經標記為1 ,所以這個時候就回溯到第二行也就是上一層從新判斷同時把標記為1 的變量還原為0,直到滿足條件輸出。題中有一個陷阱,深搜的時候是按列輸出的,不是行輸出。
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; int hang[11], n=8; int a[10][10] = { 0 }; int t = 1; void print() { printf("No. %d\n", t++); for (int i = 1; i <= 8; i++) { for (int j = 1; j <= 8; j++) { printf("%d ", a[j][i]); } printf("\n"); } } bool judge(int num) { for (int i = 1; i < num; i++) if (hang[num] == hang[i] || abs(hang[i] - hang[num]) == num - i) //判斷列和對角線 return 0; return 1; } void dfs(int num) { if (num >= 9){ print(); } for (int i = 1; i <= 8; i++) { hang[num] = i; if (a[num][i]!=1&&judge(num)) { a[num][i] = 1; dfs(num + 1); a[num][i] = 0; } } } int main() { //freopen("1.txt", "w", stdout); dfs(1); return 0; }2.3、實例3
最后在來一個dfs解決迷宮問題,就是1代表障礙,0代表通過,然后問從頭到尾一共有幾條路徑可以走到終點,這個問題同樣是dfs加回溯,就是遍歷每一個走過點的上下左右四個方向,直到最后走到終點再重新回溯就是return 1,把走過的還原為0,(因為走過的路都標記為1),最后return sum把順帶可以return的結果輸出。
/* 輸入兩個數n,m,代表迷宮的行和列 接下來輸入n行m列由0,1組成的迷宮,其中1代表障礙 求從左上角到右下角的路線個數 */ #include<stdio.h> #define N 1000//最大行列數 int mg[N][N];//存放迷宮圖 int re[N][N];//記錄之前是否走過 int n, m;//行,列 int dfs(int x, int y){//現在在(x,y)點 if(x < 0 || x > n - 1 || y < 0 || y > m - 1 || re[x][y] == 1 || mg[x][y] == 1) return 0;//走出界外或之前走過或遇到障礙 if(x == n - 1 && y == m - 1) return 1;//走到終點 re[x][y] = 1;//該點標記為走過 int sum = 0; sum += dfs(x - 1, y);//向左走 sum += dfs(x + 1, y);//向右走 sum += dfs(x, y - 1);//向上走 sum += dfs(x, y + 1);//向下走 re[x][y] = 0;//該點還原為沒有走過 return sum; } int main(){ int i, j; while(~scanf("%d%d", &n, &m)){//輸入行列 for(i = 0; i < n; i ++) { for(j = 0; j < m; j ++) scanf("%d", &mg[i][j]);//讀入迷宮圖 } printf("%d\n", dfs(0, 0));//輸出結果 } return 0; }總結
- 上一篇: 第四届蓝桥杯省赛javaB组试题解析
- 下一篇: 全排列递归算法详解