BFS(广度优先遍历)在一般的带权图中是不能解决最短路问题,了解BFS的都知道,BFS是根据节点到源节点之间的节点数遍历的,也就是先访问离源节点节点数最少的点。要使得BFS能计算最短路径,需要图结构满足所有的权值相等。否则应该使用dijkstra算法去解决最短路。
权值相等的这种情况,在解决迷宫问题的时候有很好的表现能力。因为迷宫问题满足下面几个特点:
1.迷宫采用矩阵的方式去储存的时候,矩阵中的每一个元素都是一个节点。
2.节点之间四近邻可达,或者其他的可达条件描述了节点之间的连接信息,保证了节点之间的权值是相等的。
3.节点之间是否连接是不明显的,除非你去将迷宫矩阵转化成图矩阵,在不明显的情况下,dijkstra算法就不太好理解实现。
对于这种情况的求最短路径我们一般采用BFS求最短路径,可以达到很好的效果。
在应用bfs过程中一般有一些过程是需要的,可以帮助我们理解整体的架构
用来暂存储存矩阵中的节点信息
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;
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表示墙,那么该节点如果是墙就说明不可达(就是我上面的这个判断),而在复杂的问题中可达性可能就更复杂。
就是从当前节点转移到下一个节点。
//移动的开始节点,移动的方向
//{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){
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
using namespace std;
#include
#define MAXNUM 20
#include
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 q;
q.push(start);
node next;
//结束条件为:队列为空,或者遍历到了最终节点
while(!q.empty()&&q.front()!=end){
// cout<<"hello world"<
node p = q.front();
// cout<
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<" " ;
}
cout<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) 本质就是一棵树,利用模的性质进行剪枝。
第七次ccf的第四题游戏,没有看到特别巧妙的算法,使用三维BFS数据量太大有点不好处理,而且感觉方法很鸡肋。
亡命逃窜,三维的一道模板题。
——2017.3.15