• DFS——求图的连通性问题


     DFS作为一个竞赛必学的一个知识点,怎么说我都得写一下

                遍历就相当于爆搜,只不过是搜的方式比较规整罢了。

    深度优先遍历为了避免重复访问某个顶点,可以设一个标志数组vis[i],未访问时值为0,访问一次后就改为1。

          代码实现:

    //DFS参考代码
    #include <cstdio>
    const int maxn=1010;
    int a[maxn][maxn];
    int vis[maxn];
    int n,m;
    void dfs(int u){
        printf("%d
    ",u);
        vis[u]=1;
        for(int i=1;i<=n;i++)
            if(a[u][i]==1&&vis[i]==0) dfs(i);
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            a[x][y]=a[y][x]=1;            //不同的问题可能不需要双边见图,对于坐标点,就只需要a[x][y]
    } dfs(1); return 0; }

    广度优先遍历的实现:  与深度优先遍历类似避免重复访问,需要一个状态数组 vis[n],用来存储各顶点的访问状态。

       如果 vis[i] = 1,则表示顶点 i 已经访问过;如果 vis[i] = 0,则表示顶点 i 还未访问过。初始时,各顶点的访问状态均为 0。

         代码实现:

    //BFS参考代码
    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int maxn=1010;
    int q[maxn];
    int a[maxn][maxn];
    int vis[maxn];
    int n,m;
    void bfs(int u){
        int head=0,tail=1;
        q[0]=u;
        vis[u]=1;
        while(head<tail){
            int p=q[head++];
            cout<<p<<endl;
            for(int i=1;i<=n;i++){
                if(a[p][i]==1&&vis[i]==0){
                    q[tail++]=i;
                    vis[i]=1;
                }
            }
        }
    }
    int main(){
        cin>>n>>m;
        for(int i=0;i<m;i++){
            int x,y;
            cin>>x>>y;
            a[x][y]=a[y][x]=1;        //同上,不同的问题可能不需要双边见图,对于坐标点,就只需要a[x][y]

    } bfs(1);
    return 0; }

    经典例题:

    例1油田(zoj1709 poj1562)
    题目描述:
    GeoSurvComp 地质探测公司负责探测地下油田。每次 GeoSurvComp 公司都是在一块长方形的土地上来探测油田。在探测时,他们把这块土地用网格分成若干个小方块,然后逐个分析每块土地,用探测设备探测地下是否有油田。方块土地底下有油田则称为 pocket,如果两个pocket相邻,则认为是同一块油田,油田可能覆盖多个 pocket。
    你的工作是计算长方形的土地上有多少个不同的油田。
    输入描述:
    输入文件中包含多个测试数据,每个测试数据描述了一个网格。
    每个网格数据的第一行为两个整数:m n,分别表示网格的行和列;如果m = 0,则表示输入结束,否则 1≤m≤100,1 ≤n≤100。
    接下来有m 行数据,每行数据有 n 个字符(不包括行结束符)。每个字符代表一个小方块,如果为"*",则代表没有石油,如果为"@",则代表有石油,是一个 pocket。
    输出描述:
    对输入文件中的每个网格,输出网格中不同的油田数目。如果两块不同的 pocket 在水平、垂直、或者对角线方向上相邻,则被认为属于同一块油田。每块油田所包含的 pocket 数目不会超过 100。

    思路:DFS的板子题,我们从第一个点开始,寻找它周围与它相连通的点然后打上标记,一次dfs结束就代表它已经找完与它相连通的油田,接下来的每一次dfs都是从未标记的点(也就是未找过的点,也就是未连通的点)开始遍历,如果所有的点都找完一遍了,就意味着我们遍历完一遍了,然后看看我们dfs了多少遍(代码中有cnt来记录的),也就是有多少连通块;

    代码实现&讲解:

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    using namespace std;
    const int MAXN=110;
    int a[MAXN][MAXN];
       const  int  dx[8]={-1,0,1,-1,1,-1,0,1};    //我们把它要走的八个方向的坐标都写出来 
      const  int  dy[8]={1,1,1,0,0,-1,-1,-1}; 
    int n,m,cnt;
    void dfs(int x,int y ){
        a[x][y]=0;         //找完这个点之后就打上标记,表明已找完这个点 
        for(int i=0;i<8;i++){
                 int xx=x+dx[i];           
                 int yy=y+dy[i];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&a[xx][yy]==1){   //一定要注意边界条件!!! 
                dfs(xx,yy);
            }
        } 
    }
    
    int main(){
             while(cin>>n>>m&&(n>0)){   // 这种输入是因为有多组停止输入输出,且个数未知,当读到0停止 所以n>0 
                 char c;
                 memset (a,0,sizeof(a));
                 for(int i=1;i<=n;i++){
                     for(int j=1;j<=m;j++){
                         cin>>c;
                         if(c=='@')a[i][j]=1;  // 如果为'@'说明有油田 
                         else a[i][j]=0;
                     }
                 }
                int  cnt=0;
                 for(int i=1;i<=n;i++){
                     for(int j=1;j<=m;j++){
                         if(a[i][j]==1){
                             dfs(i,j);   //每次搜完之后就表明这它已经找完所有和它联通的点了 
                             cnt++;      // 这就找完了一个油田块,油田块+1,然后就接着找其他与它不相邻的未标记的点 
                         } 
                     }
                 }
                 cout<<cnt<<endl;   //输出有多少油田块 
             }
    
    return 0;
    }

     

    例2红与黑(zoj2165 poj1979)


    题目描述:
    有一个长方形的房间,房间里的地面上布满了正方形的瓷砖,瓷砖要么是红色,要么是黑色。一男子站在其中一块黑色的瓷砖上。男子可以向他四周的瓷砖上移动,但不能移动到红色的瓷砖上,只能在黑色的瓷砖上移动。
    本题的目的就是要编写程序,计算他在这个房间里可以到达的黑色瓷砖的数量。
    输入描述:
    输入文件中包含多个测试数据。
    每个测试数据的第 1 行为两个整数 W 和 H,分别表示长方形房间里 x 方向和 y 方向上瓷砖的数目。W 和 H 的值不超过20。
    接下来有 H 行,每行有 W 个字符,每个字符代表了瓷砖的颜色,这些字符的取值及含义为:
    1) '.' - 黑色的瓷砖;
    2) '#' - 红色的瓷砖;
    3) '@' - 表示该位置为黑色瓷砖,且一名男子站在上面,注意每个测试数据中只有一个'@'符号。
    输入文件中最后一行为两个 0,代表输入文件结束。
    输出描述:
    对输入文件中每个测试数据,输出占一行,为该男子从初始位置出发可以到达的黑色瓷砖的数目(包括他初始时所处的黑色瓷砖)。

    思路:这个题与上一个题的不同之处在于:上一个题求的是有多少连通块,而这个题求的是一个连通块有多大,我们只需要在最后遍历一遍所有的点,记录我们到达的点

     代码实现&讲解:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int MAXN=1010;
    int w,h,cnt;
    int a[MAXN][MAXN];
    const int dx[4]={0,0,-1,1};
    const int dy[4]={1,-1,0,0};    //预处理出可走的四个方向 
    void dfs(int x,int y){
        a[x][y]=5;       // 我们从我们可以走的点中找我们所走过的点,然后打上标记 
       for(int i=0;i<4;i++){
             int xx=x+dx[i];
             int yy=y+dy[i];
             if(a[xx][yy]==1&&xx>=1&&xx<=h&&yy>=1&&yy<=w){
                 dfs(xx,yy);
             }
        }
    }
    int  main() {
        int x,y;
        memset(a,1,sizeof(a));
            while(cin>>w>>h&&(w>0)){     //h为行,w为列 
                for(int i=1;i<=h;i++){
                    for(int j=1;j<=w;j++){
                    char c;
                    cin>>c;
                    if(c=='.')a[i][j]=1;  //这是我们可以走的点 
                    if(c=='#')a[i][j]=0;   //这是我们不能走的点 
                    if(c=='@'){     //这是我们的起点 
                        x=i;       //标记号我们的起点,dfs就从这个点开始 
                        y=j;
                    }
                }
            }
            dfs(x,y);
            cnt=0;
            for(int i=1;i<=h;i++){
                for(int j=1;j<=w;j++){
                    if(a[i][j]==5)cnt++;   //数我们走过的点
                }
            }
            cout<<cnt<<endl;       //输出结果 
        }  
        return 0;
    }

    End~

  • 相关阅读:
    [数据结构与算法 01] 什么是数据结构?什么是算法?联系是什么?常用的数据结构/算法有?
    程序员面试金典-面试题 16.05. 阶乘尾数
    程序员面试金典-面试题 16.04. 井字游戏
    程序员面试金典-面试题 16.02. 单词频率
    程序员面试金典-面试题 16.01. 交换数字
    程序员面试金典-面试题 10.11. 峰与谷
    程序员面试金典-面试题 10.10. 数字流的秩
    程序员面试金典-面试题 10.09. 排序矩阵查找
    程序员面试金典-面试题 10.05. 稀疏数组搜索
    程序员面试金典-面试题 10.03. 搜索旋转数组
  • 原文地址:https://www.cnblogs.com/xiaoK-778697828/p/9671457.html
Copyright © 2020-2023  润新知