图论DFS与BFS

来源:互联网 发布:ios sql软件 编辑:程序博客网 时间:2024/05/22 05:00

  • 用BFS求最短路

如迷宫中,用BFS遍历整张图,逐步计算出起点到每个节点的最短路径dis[i][j],以及这些最短路径上的每一个节点的“前一个节点”。具体是否需要存储视题目而定,如只需要求最短路径长度,那就只需要存储dis即可;如果要输出具体的路径,那就需要存储path了。

一、模板

int vis[maxn];bool can(int x,int y){        if(vis[x][y] == -1 && 下一步在题目中是允许走过去的) return true;        else return false;//如果这个点已经访问过了,那么一定是已经从最短的路径过来的了}void bfs(){        memset(vis,-1,sizeof(vis));//这里初始化为-1的目的是可以同时判断这里是不是已经得到了最优的解了        建立队列q并且让出发点入队列;        vis[出发点]=0;//不可以再返回出发点了        while(!q.empty()){//只要队列还没有空或者说是还有没有遍历完所有可能的情况那么都还要继续走下去                now=q.front();                q.pop();//取队列的首位,并删除首位                for(列举所有可能的情况){                        获取新状态new;                        if(can(new)){//如果事先不把所有的情况想好以及判断条件想好,那么后期是很容易错的                                if(满足题中所给条件){//                                        输出答案or更新答案;                                        return;                                }                                vis[new] = vis[now]+1;                                q.push(new);//新状态入队列                        }                }        }        说明还没有找到满足答案的路径;        return;}

二、输出最短路径

#include <bits/stdc++.h>using namespace std;struct Node{    int x, y;    Node(int a=0, int b=0) :x(a), y(b){}};int G[maxn][maxn];//记录迷宫int dis[maxn][maxn];//记录该节点到起点的最短距离Node path[maxn][maxn];//记录其中的父亲节点const int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};bool isleg(int x, int y){//判断是否满足条件    return (x>=0&&y>=0&&x<maxn&&y<maxn &&dis[x][y]==-1&&!G[x][y]);}void bfs(){    queue<Node>Q;    Q.push(Node(0, 0));//起点入队    memset(dis,-1,sizeof(dis));    dis[0][0]=0;    while(!Q.empty()){        Node u=Q.front();Q.pop();        //如果此时的u可以判断为终点,那么此时就可以结束循环。        for (int i=0;i<4;i++){            int dx=u.x+dir[i][0];            int dy=u.y+dir[i][1];            if (isleg(dx, dy)){                dis[dx][dy]=dis[u.x][u.y]+1;                path[dx][dy]=u;//将该节点指向其父亲节点                Q.push(Node(dx,dy));            }        }    }}void printPath(Node u){    if (!u.x&&!u.y){//如果节点u里面存储的是起点儿子        printf("(0,0)\n");        return;    }    printPath(path[u.x][u.y]);    printf("(%d,%d)\n", u.x, u.y);}int main(){    for(int i=0;i<maxn;i++)        for(int j=0;j<maxn;j++)            scanf("%d",&G[i][j]);    bfs();    printPath(Node(4,4));//printf("%d\n", dis[4][4]);    return 0;}

三、输出最短路径长度

#include <bits/stdc++.h>using namespace std;typedef pair<int,int> P;int N,M;char mz[105][105];int dis[105][105];int ans=0;const int dir[4][2]={{0,1},{0,-1},{-1,0},{1,0}};int BFS(int c,int l){    queue<P>que;    que.push(P(c,l));    while(!que.empty())    {        c=que.front().first,l=que.front().second;//Tmp=Queue.Top        que.pop();        if(mz[c][l]=='G')                return dis[c][l];//结束条件:找到终点        for(int i=0;i<4;i++)//状态扩展,列出下一层的所有可能状态        {            int x=c+dir[i][0];            int y=l+dir[i][1];            if(x<0||y<0||x>=N||y>=M)//不符合条件                continue;            if(!dis[x][y]&&mz[x][y]!='#')//状态合法,则生成新状态NEXT            {                dis[x][y]=dis[c][l]+1;//NEXT.STEP=Tmp.STEP+1                que.push(P(x,y));//加入队列            }        }    }    return -1;}int main(){    memset(dis,0,sizeof(dis));    scanf("%d%d",&N,&M);    for(int i=0;i<N;i++)        scanf("%s",mz[i]);            for(int i=0;i<N;i++)        for(int j=0;j<M;j++)            if(mz[i][j]=='S') ans=BFS(i,j);                printf("%d\n",ans);    return 0;}


四、题目模拟

#include <bits/stdc++.h>using namespace std;const int maxn=1000+10;struct node{    int x,y;    node(int x=0,int y=0):x(x),y(y){}}path[maxn][maxn];int mg[maxn][maxn];int dis[maxn][maxn];int dir[4][2]={{-1,0},{0,-1},{1,0},{0,1}};bool iscan(int dx,int dy){    if(dis[dx][dy]==-1&&mg[dx][dy]==1) return true;    else return false;}void bfs(){    memset(dis,-1,sizeof(dis));    dis[0][0]=0;    queue<node>q;    q.push(node(0,0));    while(!q.empty()){        node u=q.front();        q.pop();        for(int i=0;i<4;i++){            int dx=u.x+dir[i][0];            int dy=u.y+dir[i][1];            if(iscan(dx,dy)){                dis[dx][dy]=dis[u.x][u.y]+1;                path[dx][dy]=u;                q.push(node(dx,dy));            }        }    }}//递归输出路径void print_path1(node u){    if(u.x==0&&u.y==0){        printf("(0,0) ");        return;    }    print_path1(path[u.x][u.y]);//二者顺序不能颠倒,否则输出的是从终点到起点    printf("(%d,%d) ",u.x,u.y);}//利用栈输出路径void print_path2(node u){    stack<node>s;    for(;;){        s.push(u);        if(dis[u.x][u.y]==0) break;        u=path[u.x][u.y];    }    while(!s.empty()){        printf("(%d,%d) ",s.top().x,s.top().y);        s.pop();    }}int main(){    freopen("input.txt","r",stdin);    int m,n;    scanf("%d%d",&m,&n);    for(int i=0;i<m;i++)        for(int j=0;j<n;j++)            scanf("%d",&mg[i][j]);    bfs();    print_path1(node(5,5));    printf("%d\n",dis[5][5]);    return 0;}

  • 用DFS求连通块

求多维数组连通块的过程也被称为种子填充。种子填充算法常用四连通域和八连通域技术进行填充操作。
从区域内任意一点出发,通过上、下、左、右四个方向到达区域内的任意像素。用这种方法填充的区域就称为四连通域;这种填充方法称为四向连通算法。
从区域内任意一点出发,通过上、下、左、右、左上、左下、右上和右下八个方向到达区域内的任意像素。用这种方法填充的区域就称为八连通域;这种填充方法称为八向连通算法。

可以用一个二重循环或者常量数组或者写4条/8条DFS调用来搜索全部方向

#include <bits/stdc++.h>using namespace std;const int maxn=1000+10;char yt[maxn][maxn];int vis[maxn][maxn];int dir[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};//8个方向int m,n,ans=0;void dfs(int i,int j,int ans){    if(i<0||j<0||i>=m||j>=n) return;//越界    if(vis[i][j]!=0||yt[i][j]!='@') return;//不满足题目条件    vis[i][j]=ans;    for(int t=0;t<8;t++){        int di=i+dir[t][0];        int dj=j+dir[t][1];        dfs(di,dj,ans);    }}int main(){    freopen("input.txt","r",stdin);    scanf("%d%d",&m,&n);    memset(vis,0,sizeof(vis));    for(int i=0;i<m;i++)        scanf("%s",yt[i]);    for(int i=0;i<m;i++)        for(int j=0;j<n;j++)            if(yt[i][j]=='@'&&vis[i][j]==0) dfs(i,j,++ans);    /*    for(int i=0;i<m;i++){        for(int j=0;j<n;j++)            printf("%d ",vis[i][j]);        printf("\n");    }    */    printf("%d\n",ans);}

  • 拓扑排序
在有向图中,使得每一条有向边(u,v)对应的u都排在v的前面。不难发现:如果有向图中存在有向环,则不存在拓扑排序,反之则存在。不包含有向环的有向图称作有向无环图。可以借助DFS完成拓扑排序,在访问完一个节点之后把它加到当前拓扑序列的首部。因为最后访问的节点才是出度为0的节点,它才是实际意义上的最后一个访问的节点,所以算法是倒序求实际意义上的拓扑序列。
#include <bits/stdc++.h>using namespace std;const int maxn=1000+10;//图的相关数据int G[maxn][maxn];char vex[maxn];int vexnum,arcnum;int vis[maxn],topo[maxn],len;//构建图void build(char a,char b){    int posa,posb;    for(int i=0;i<vexnum;i++){        if(vex[i]==a) posa=i;        if(vex[i]==b) posb=i;    }    //printf("%d %d\n",posa,posb);    G[posa][posb]=1;}bool dfs(int u){    vis[u]=-1;//访问过的标记    for(int i=0;i<vexnum;i++) if(G[u][i]){        if(vis[i]==-1) return false;//存在有向环,失败退出        if(vis[i]==0&&dfs[i]==false) return false;//访问的子节点中存在有向环的情形    }    vis[u]=1;topo[--len]=u;//访问完所有子孙,并成功返回,将节点从后到前地加到拓扑数组中    return true;}bool toposort(){    len=vexnum;    memset(vis,0,sizeof(vis));    //0代表未访问,-1代表正在访问,还没有返回,1代表已经访问并且还递归访问了它的所有子孙    for(int i=0;i<vexnum;i++) if(vis[i]==0){        if(dfs(i)==false) return false;    }    return true;}int main(){    //freopen("input.txt","r",stdin);    //输入并建造图的过程    memset(G,0,sizeof(G));    scanf("%d%d",&vexnum,&arcnum);    for(int i=0;i<vexnum;i++)        scanf(" %c",&vex[i]);    for(int i=0;i<arcnum;i++){        char buf[maxn];        scanf("%s",buf);        char a,b,c,d,e;        sscanf(buf,"%c%c%c%c%c",&a,&b,&c,&d,&e);        //printf("%c %c\n",b,d);        build(b,d);    }    //开始拓扑排序    if(toposort()){        for(int i=0;i<vexnum;i++)            printf("%c ",vex[topo[i]]);    }    return 0;}


原创粉丝点击