算法总结—深度优先搜索DFS

深度优先搜索(DFS)

往往利用递归函数实现(隐式地使用栈)。

深度优先从最开始的状态出发,遍历所有可以到达的状态。由此可以对所有的状态进行操作,或列举出所有的状态。

1.poj2386 Lake Couting

题意:八连通被认为连接在一起,求总共有多少个水洼?

Sample Input:

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

Sample Output

3

思路:从任意W开始,一次DFS把连通的.全部变为W,遍历图直到没有.为止,进行DFS次数即为水洼的个数。
代码:
 1 #include<iostream>
 2 using namespace std;
 3 int N, M;
 4 char pond[100][100]; //global variable
 5 void dfs(int i, int j){ // 注意参数设计,要不要返回值 
 6     pond[i][j] = '.';
 7     for(int dx = -1; dx <= 1; ++dx){ //八连通遍历方式,四连通往往事先开好数组,见后续题目
 8         for(int dy = -1; dy <= 1; ++dy){
 9             int x = i + dx, y = j + dy;
10             if(x >= 0 && x < N && y >= 0 && y < M && pond[x][y] == 'W'){
11                 dfs(x,y);
12             }
13         }
14     }
15     return;  
16 }
17 int main(){
18     cin >> N >> M;
19     for(int i = 0; i < N; ++i){
20         for(int j = 0; j < M; ++j){
21             cin >> pond[i][j];
22         }
23     }
24 
25     int count = 0;
26     for(int i = 0; i < N; ++i){
27         for(int j = 0; j < M; ++j){
28             if(pond[i][j] == 'W'){
29                 dfs(i,j);
30                 count ++;    
31             }
32         }
33     }
34     cout << count << endl;
35 }

2.poj1979 Red and Black

题意:@表示起点,可以上下左右四方向走,"."为黑色,可以走,“#”为红色,不可以走,问可以到达多少个黑色位置?

Sample Input

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

Sample Output

45
59
6
13
思路:从起点处DFS遍历即可,当前点为“.”则将其改为“#”,result++,并以该点出发继续遍历。
代码:
 1 #include<iostream>
 2 using namespace std;
 3 char rect[20][20];
 4 int result = 0;  //全局的result
 5 int dx[4] = {-1,0,0,1};  //四连通处理方法
 6 int dy[4] = {0,1,-1,0};
 7 int W = 1, H = 1;
 8 void dfs(int sx, int sy){
 9     rect[sx][sy] = '#';
10     result++;
11     for(int i = 0; i < 4; ++i){ 
12         int x = sx + dx[i], y = sy + dy[i];
13         if(x >= 0 && x < H && y >= 0 && y < W && rect[x][y] == '.'){
14             dfs(x,y);
15         }
16     }
17     return ;
18 }
19 int main(){
20     while(W != 0 && H != 0){
21         cin >> W >> H;
22         if(W == 0 && H == 0){
23             return 0;
24         }
25         int sx, sy;
26         for(int i = 0; i < H; ++i){
27             for(int j = 0; j < W; ++j){
28                 cin >> rect[i][j];
29                 if(rect[i][j] == '@'){
30                     sx = i;
31                     sy = j;
32                 }
33             }
34         }
35         dfs(sx, sy);
36         cout << result << endl;
37         result = 0;
38     }
39     return 0;
40 } 

3.aoj0118 Property Distribution

题意: 苹果是@,梨是#, 蜜柑是*。 四连通且相同品种在一个区域。计算每组数据区域的个数。

Sample Input:

10 10
####*****@
@#@@@@#*#*
@##***@@@*
#****#*@**
##@*#@@*##
*@@@@*@@@#
***#@*@##*
*@@@*@@##@
*@*#*@##**
@****#@@#@
0 0

Output for the Sample Input

33
思路:类似第一题的池塘数个数的思路,DFS遍历,将遍历完毕的节点改为不同于上述三种标志的第四种标志,如“X”,
并且在DFS函数中加入标志参数用于判断同类水果区域,一次DFS,result++,当所有标志为X时,遍历结束。
代码:
 1 #include<iostream>
 2 using namespace std;
 3 char garden[100][100];
 4 int H = 1, W = 1;
 5 int result = 0;
 6 int dx[4] = {-1,0,0,1};
 7 int dy[4] = {0,1,-1,0};
 8 void dfs(int sx, int sy,char c){ //加入char判断是否同一类
 9     garden[sx][sy] = 'x';
10     for(int i = 0; i < 4;++i){
11         int x = sx + dx[i], y = sy + dy[i];
12         if(x >= 0 && x < H && y >= 0 && y < W && garden[x][y] == c){
13             dfs(x,y,c);
14         }
15     }
16     return;
17 }
18 int main(){
19     while(H != 0 && W != 0){
20         cin >> H >> W;
21         if(H == 0 && W == 0){
22             return 0;
23         }
24         for(int i = 0; i < H; ++i){
25             for(int j = 0; j < W; ++j){
26                 cin >> garden[i][j];
27             }
28         }
29         for(int i = 0; i < H; ++i){
30             for(int j = 0; j < W; ++j){
31                 if(garden[i][j] != 'x'){
32                     dfs(i,j,garden[i][j]);
33                     result++;   
34                 }
35             }
36         }
37         cout << result << endl;
38         result = 0;
39     }
40 }
4.aoj0033 Ball
题意:
A管进球,B,C管出球,给定如球顺序,判断能否移动挡板,使得B,C出球顺序均为从下往上标号递增。


Sample Input

2
3 1 4 2 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1

Output for the Sample Input

YES
NO
思路:DFS遍历,一个参数记录当前到第几个球,另外两个记录当前B,C顶端球得数值,以便于比较。
代码:
 1 #include<iostream>
 2 using namespace std;
 3 int A[10];
 4 bool dfs(int start, int topB, int topC){  //有返回值的DFS
 5     if(start == 9){  //最后一个球啦
 6         if(A[start] > topB || A[start] > topC){
 7             return true;
 8         }
 9         else{
10             return false;
11         }
12     }
13     bool b1 = false, b2 = false;
14     if(A[start] > topB){  //B可以放,放进去试
15         b1 = dfs(start+1, A[start], topC);
16     }
17     if(A[start] > topC){ //C可以放,放进去试
18         b2 = dfs(start+1,topB,A[start]);
19     }
20     return (b1 || b2 );   //B,C都不可以放的时候,返回false,否则true
21 }
22 int main(){
23     int N;
24     cin >> N;
25     for(int i = 0; i < N; ++i){
26         for(int j = 0; j < 10; ++j){
27             cin >> A[j];
28         }
29         bool result = dfs(0,0,0);
30         if(result == true){
31             cout << "YES" << endl; 
32         }
33         else{
34             cout << "NO" <<endl; 
35         }
36     }
37     return 0;
38 }

 5. poj3009 Curling 2.0

题意:

可以沿上下左右走到障碍物,碰到障碍物后停下,障碍物消失,然后可以继续出发,出界算失败,超过十步算失败,问能否从S到G,

不能输出-1,能输出最少步数。(0路径,1障碍物,2起点,3终点)

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1

思路:这种走到底的题目也可以用DFS,设计成无返回值,到达3后比较当前值与已有最小值的大小。注意走到底的写法(while)

代码:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<limits.h>
 4 using namespace std;
 5 int board[21][21];
 6 int W = 1, H = 1;
 7 int dx[4] = {-1,0,0,1};
 8 int dy[4] = {0,1,-1,0};
 9 int sx, sy,minStep = INT_MAX;
10 void dfs(int sx,int sy,int step){    //设计成无返回值,当board[i][j] == 3时比较当前 step+1 与最小的step并更新 
11     if(step >= 10){     //剪枝 
12         return;
13     }
14     for(int i = 0; i < 4;++i){
15         int x = sx + dx[i], y = sy + dy[i];
16         if(x >= 0 && x < H && y >=0 && y < W && board[x][y] != 1){
17             while(x >= 0 && x < H && y >=0 && y < W && board[x][y] != 1){ //走到底的判断 
18                 if(board[x][y] == 3){
19                     if(step + 1 < minStep){
20                         minStep = step + 1;
21                         break;
22                     }
23                 }
24                 x += dx[i];
25                 y += dy[i];
26                 if(board[x][y] == 1){
27                     board[x][y] = 0;
28                     dfs(x - dx[i], y - dy[i], step + 1);
29                     board[x][y] = 1;    // 恢复状态 
30                 }
31             }
32         }
33     }
34     return;
35 }
36 
37 int main(){
38     while(W != 0 && H != 0){
39         cin >> W >> H;
40         if(W == 0 && H == 0){
41             return 0;
42         }
43         memset(board,0,sizeof(board));  
44         for(int i = 0; i < H; ++i){
45             for(int j = 0; j < W; ++j){
46                 cin >> board[i][j];
47                 if(board[i][j] == 2){
48                     sx = i;
49                     sy = j;
50                 }
51             }
52         }
53         dfs(sx,sy,0);
54         if(minStep == INT_MAX){
55             cout << "-1" << endl;
56         }
57         else{
58             cout << minStep << endl;    
59         }
60         minStep = INT_MAX;
61     }
62     return 0;
63 }

6.poj1321 棋盘问题

题意:n*n矩阵形状的棋盘(“#”为可摆放棋盘区域,“.”为不可摆放空白区域),要摆放k个棋子,同行同列不能有两个,共多少种方案?

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1

思路:DFS的思路,一行一行的确定摆放位置,开一个数组place[8]记录哪一列已经有摆放。
注意k如果小于N时,可以有某一行不摆放元素,所以代码24行 DFS(row+1,num) 必须添加。

代码:
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int N = 1,K = 1;
 5 char chess[8][8];
 6 int result = 0;
 7 int placed[8] = {0};  //记录该列是否有摆放
 8 void dfs(int row,int num){
 9     if(num == K){   //摆放成功,方案数++
10         result++;
11         return; 
12     }
13     if(row == N){
14         return;
15     }
16 
17     for(int i = 0; i < N; ++i){
18         if(chess[row][i] == '#' && placed[i] == 0){
19             placed[i] = 1;
20             dfs(row + 1, num + 1);
21             placed[i] = 0;
22         }
23     }
24     dfs(row+1,num);  //!容易忽略
25 
26 }
27 int main(){
28     while(N != -1 && K != -1){
29         cin >> N >> K;
30         if(N == -1 && K == -1){
31             return 0;
32         } 
33         memset(chess,0,sizeof(chess));
34         memset(placed,0,sizeof(placed));
35         for(int i = 0; i < N;++i){
36             for(int j = 0; j < N; ++j){
37                 cin >> chess[i][j];
38             }
39         }
40         dfs(0,0);
41         cout << result << endl;    
42         result = 0;    
43     }
44 
45 } 

 

 
posted @ 2016-06-13 13:39  wangxiaobao1114  阅读(997)  评论(0编辑  收藏  举报