广度优先遍历解决最短路问题

BFS(广度优先遍历)在一般的带权图中是不能解决最短路问题,了解BFS的都知道,BFS是根据节点到源节点之间的节点数遍历的,也就是先访问离源节点节点数最少的点。要使得BFS能计算最短路径,需要图结构满足所有的权值相等。否则应该使用dijkstra算法去解决最短路。

权值相等的这种情况,在解决迷宫问题的时候有很好的表现能力。因为迷宫问题满足下面几个特点:
1.迷宫采用矩阵的方式去储存的时候,矩阵中的每一个元素都是一个节点。
2.节点之间四近邻可达,或者其他的可达条件描述了节点之间的连接信息,保证了节点之间的权值是相等的。
3.节点之间是否连接是不明显的,除非你去将迷宫矩阵转化成图矩阵,在不明显的情况下,dijkstra算法就不太好理解实现。
对于这种情况的求最短路径我们一般采用BFS求最短路径,可以达到很好的效果。

在应用bfs过程中一般有一些过程是需要的,可以帮助我们理解整体的架构

1.定义结构体

用来暂存储存矩阵中的节点信息

typedef struct Node{
    int x;
    int y;
    Node(){x=0;y=0;}
    Node(int x,int y){
        this->x = x;
        this->y = y;
    }
    bool operator!=(const Node node)const {

        if(x!=node.x||y!=node.y){
            return true;
        }
        return false;
    }
}node;

2.判断是否可达

bool judge(node node){
    //1~n属于合法范围
    if(node.x<1||node.x>n) return false;
    if(node.y<1||node.y>n) return false;
    if(graph[node.x][node.y]==1) return false;//不是路
    return true;
}

由于这个题目比较简单,所以判断节点是否越界和判断节点是否可达我写在了一起。分开写更好理解。越界的情况基本都是相同的,但是是否可达确实可变的,比如在简单的迷宫问题中,0表示路,1表示墙,那么该节点如果是墙就说明不可达(就是我上面的这个判断),而在复杂的问题中可达性可能就更复杂。

3.行走函数

就是从当前节点转移到下一个节点。

//移动的开始节点,移动的方向
//{0,1,2,3}分别是{上,左,下,右}
node walk(node start,int direction){
    if(direction==0){
        return node(start.x,start.y-1);
    }
    if(direction==1){
        return node(start.x-1,start.y);
    }
    if(direction==2){
        return node(start.x,start.y+1);
    }
    return node(start.x+1,start.y);
}

简单问题中这样写很鸡肋,但是在复杂问题中可以很好的方便思考。根据题目要求的行走规则不同,这个函数就需要变换。

4.BFS函数

有上面几个格式化的函数,我们就可以很方便的写出伪代码

void BFS(node start){
    q.push(start);//第一个节点入队
    while(!q.empty()){
        p = q.front();q.pop();//出队
        if p==end return;//如果为目的节点则结束BFS
        //对于所有的与p相连的节点v
        for 所有的可能方向i{
            下一个节点:v=walk(p,i)
            if judge(v){//如果v满足条件
                q.push(v);//v入队
            }
        }
    }
}

大致的BFS解决最短路问题就分为这几个模块。下面贴出所有的代码,问题就是在一个迷宫中找到最短路,其中0表示路,1表示墙。

对于最短路径的长度是使用dp数组储存,路径使用parent数据记录,记录的是其最短路径时的父节点,需要输出的时候,直接回溯就行。

//============================================================================
// Name        : BFSSP.cpp
// Author      : SELOUS
// Version     :
// Copyright   : Your copyright notice
// Description : adopt BFS solving Shortest Path
//============================================================================

/**features:
 * 1.矩阵中的每个元素代表一个节点
 * 2.节点的连接节点一般是题目规定的,常见的如四近邻,八近邻,也可以看做是状态转换。
 * 3.下一个节点需要满足的条件为,节点可达,且不能过界
 * 4.使用BFS求最短路径,需满足每条边的权重相同
 * */

#include <iostream>
using namespace std;

#include <queue>
#define MAXNUM 20

#include <cstring>

int graph[MAXNUM][MAXNUM];//储存图的信息

int dp[MAXNUM][MAXNUM];//对应节点到(0,0)的最短路径

int n;

typedef struct Node{
    int x;
    int y;
    Node(){x=0;y=0;}
    Node(int x,int y){
        this->x = x;
        this->y = y;
    }
    bool operator!=(const Node node)const {

        if(x!=node.x||y!=node.y){
            return true;
        }
        return false;
    }
}node;

node parent[MAXNUM][MAXNUM];//记录最短路径的父节点
node end(5,1);//初始化结束节点
bool judge(node node){
    //1~n属于合法范围
    if(node.x<1||node.x>n) return false;
    if(node.y<1||node.y>n) return false;

    if(graph[node.x][node.y]==1) return false;//不是路

    return true;
}

//移动的开始节点,移动的方向
//{0,1,2,3}分别是{上,左,下,右}
node walk(node start,int direction){

    if(direction==0){
        return node(start.x,start.y-1);
    }
    if(direction==1){
        return node(start.x-1,start.y);
    }
    if(direction==2){
        return node(start.x,start.y+1);
    }
    return node(start.x+1,start.y);
}

void bfs(node start){
    int i;
    queue<node> q;
    q.push(start);
    node next;
    //结束条件为:队列为空,或者遍历到了最终节点
    while(!q.empty()&&q.front()!=end){
//      cout<<"hello world"<<endl;
        node p = q.front();
//      cout<<p.x<<" "<<p.y<<endl;
        q.pop();
        for(i=0;i<4;i++){
            next = walk(p,i);
            if(dp[next.x][next.y]<0&&judge(next)){
                dp[next.x][next.y] = dp[p.x][p.y]+1;//路径为父节点+1
                parent[next.x][next.y] = p;//父节点为节点p;
                q.push(next);//将该节点入队列
            }
        }
    }
}

void printDP(){
    int i,j;
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            cout<<dp[i][j]<<" ";
        }
        cout<<endl;
    }
}

int main() {

    cin>>n;
    int i,j;
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            cin>>graph[i][j]; //输入
        }
    }
    node start(1,1);//初始化开始节点
    memset(dp,-1,sizeof(dp));
    dp[start.x][start.y] = 0;//到本节点的距离为0
    parent[start.x][start.y] = node(0,0);//父节点为0
    bfs(start);
    printDP();
    return 0;
}

一些相关问题的链接:
hdu1429 胜利大逃亡(续) (广搜+状态压缩)
HDOJ 1226 超级密码(bfs) 本质就是一棵树,利用模的性质进行剪枝。

三维BFS

第七次ccf的第四题游戏,没有看到特别巧妙的算法,使用三维BFS数据量太大有点不好处理,而且感觉方法很鸡肋。
亡命逃窜,三维的一道模板题。

——2017.3.15

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值