• 算法总结—深度优先搜索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 } 
     
  • 相关阅读:
    JavaFX学习曲线日记2:声明用户接口
    深入学习JavaFX脚本语言
    JavaFX学习曲线日记2:声明用户接口
    JavaFX学习曲线日记1:探索JavaFX脚本
    深入学习JavaFX脚本语言
    略谈数学中的映射在其它领域的踪迹
    Using Swing's Pluggable Look and Feel
    JavaFX学习曲线日记1:探索JavaFX脚本
    赖勇浩:应对多核编程革命
    Using Swing's Pluggable Look and Feel
  • 原文地址:https://www.cnblogs.com/wangxiaobao/p/5580361.html
Copyright © 2020-2023  润新知