您的位置:首页 > 其它

改进基于优先队列的最短路径搜索『洪水流思想的体现』

2015-07-22 17:09 239 查看
这问题是在博客上看到的,为此学习一下,然后在他的基础上,修改完成迷宫洪水流算法的实现

题目是

Problem Description
Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is described as a N * M (N, M <= 200) matrix. There are WALLs, ROADs, and GUARDs in the prison.

Angel's friends want to save Angel. Their task is: approach Angel. We assume that "approach Angel" is to get to the position where Angel stays. When there's a guard in the grid, we must kill him (or her?) to move into the grid. We assume that we moving up, down, right, left takes us 1 unit time, and killing a guard takes 1 unit time, too. And we are strong enough to kill all the guards.

You have to calculate the minimal time to approach Angel. (We can move only UP, DOWN, LEFT and RIGHT, to the neighbor grid within bound, of course.)

Input
First line contains two integers stand for N and M.

Then N lines follows, every line has M characters. "." stands for road, "a" stands for Angel, and "r" stands for each of Angel's friend.

Process to the end of the file.

Output
For each test case, your program should output a single integer, standing for the minimal time needed. If such a number does no exist, you should output a line containing "Poor ANGEL has to stay in the prison all his life."

Sample Input
7 8
#.#####.
#.a#..r.
#..#x...
..#..#.#
#...##..
.#......
........

Sample Output
13


先给出博主的算法

http://www.cnblogs.com/heqinghui/archive/2013/07/30/3225407.html

源代码

#include <iostream>
#include <queue>
#include <algorithm>
#include <fstream>
#include <string>
using namespace std;

typedef struct p
{
int x,y,t;
bool operator < (const p &a) const
{
return t>a.t;
}
}Point;

char map[20][20];
Point start;
int n,m;
int dir[][2]={{1,0},{-1,0},{0,1},{0,-1}};

void show()
{
cout<<endl;
for(int i=0;i<n;++i)
cout<<map[i]<<endl;
}
int bfs()
{
priority_queue <Point> que;
Point cur,next;
int i;

map[start.x][start.y]='#';
que.push(start);
show();

while(!que.empty())
{
cur=que.top();
que.pop();
for(i=0;i<4;++i)
{
next.x=cur.x+dir[i][0];
next.y=cur.y+dir[i][1];
next.t=cur.t+1;

if( next.x<0 || next.x>=n || next.y<0 || next.y>=m)
continue;
if(map[next.x][next.y] == '#')
continue;
if(map[next.x][next.y]=='r')
return next.t;
if(map[next.x][next.y]=='.')
{
map[next.x][next.y]='#';
que.push(next);
}
else if(map[next.x][next.y]=='x')
{
map[next.x][next.y]='#';
next.t++;
que.push(next);

}
show();
}
}

return -1;

}

int main()
{
freopen("test.txt","r",stdin);
int i,ans;
char* p;

cin>>n>>m;
for (i=0;i<n;++i)
{
scanf("%s",map[i]);
if(p=strchr(map[i],'a'))
{
start.x=i;
start.y=p-map[i];
start.t=0;
}
//printf("%s\n",map[i]);
}
ans=bfs();
cout<<"ans "<<ans<<endl;
return 0;

}


本质就是求a到r的最短路径,区别于一般的最短路径,这里通过x是要时间加倍的。

这里研究一下,为什么使用这种优先队列可以找到最短路径

如何实现这个搜索过程

回溯法得到的不一定是最小路径,只有用回溯找到所有路径比较才还能得到最短路径。

这里面会涉及到stl的priority_queue这个类,这是一个涉及权重的队列,

首先将这个类研究一下,以后涉及权重的就可以使用这个类了

这里研究一下,为什么使用这种优先队列可以找到最短路径

这个算法的基本思路就是 从cur到next去两点之间找权重(时间)最短的路径

利用priorit_queue来查找

在每次走过一个点都将此点标志为走过;

第一次

从开始点出发,进入while循环

此时cur为起始点,队列为空,将起始点标记为“#”



开始在cur上下左右寻找可走路径,向下找到后,压入队列,并标志“#”

四次寻找结束。



将找到的两个路径,入队列,

第二次

在队列中找到t最小的点,作为cur点

此时两个一样,看看

top出来的是左边那个点(2,2),top出来之后将这个点从队列中pop掉,表示这个点已经走过

再次以cur为中心,寻找周围的有效点并压队列,此时上次没有选中的点(1,1)还在队列里面,因为如果此路不通还要返回这个点

第三次

此时在利用优先级判断,选择下一个要走的点



此时在队列里面,优先级最高的反而编程上一次没有选择的,就是点(1,1),

此时将(1,1)点top出来,并且pop掉,cur变为点(1,1)

这时有点像,从出发点向四个方向发散出去,每个方向都要搜索。

搜索cur周围点之后

标记地图为



进入下一步,优先判决

第四次

此时队列里面的点处于同一优先级,

这里会有一种情况,因为处于边上的点(0,1),即使选择它,因为没有下面的点,所以pop之后就不存在了,

就会继续往下搜索,

此时系统选择的是点(2,1)为cur,再将cur四个方向有效点压队列。

图示为



进入下一次判决

第五次

此时将之前优先级低的top出来,并pop掉,

选择的是点(0,1),由于cur点周围没有有效路径,所以,没有新的有效点压入,所以这个方向就不再搜索了

。。。。。

反复以往,直到先遇到X点

在cur点为(3,4)是搜索到上面有x点,此时权重需要多加一个一

继续 直到cur点为(2,6)时,检测到r的位置,此时,返回权重就是最短的时间路程

对于整体的各个思路,这好像是一个广度搜索,和回溯法的深度优先不太一样,基于此

是否可以用树来表示搜索过程

先看一个最简单的模型



标出路径



优先队列的搜索树图

括号里为(X,Y.priority)



所以其基本思路就是从出发点开始向外扩展,利用优先队列,存储扩展时的路长,直到找到目标点。

这个可以计算出路径的长度,对于路径的走法,

这种方法 非常像洪水流算法



然后从b回溯

再看一个例子



两条路径都是可以找到最短的

这种洪水的思想也就可以用上面优先队列来实现

所以 如果将每次压入队列的点的权重保存到地图中,这样就可以完成洪水流的操作了

修改函数为

int bfs()
{
priority_queue <Point> que;
Point cur,next;
int i;
char db;

//map[start.x][start.y]='#';
que.push(start);
show();

while(!que.empty())
{
cur=que.top();
que.pop();
for(i=0;i<4;++i)
{
next.x=cur.x+dir[i][0];
next.y=cur.y+dir[i][1];
next.t=cur.t+1;

if( next.x<0 || next.x>=n || next.y<0 || next.y>=m)
continue;
if(map[next.x][next.y] == '#')
continue;
if(map[next.x][next.y]=='r')
return next.t;
if(map[next.x][next.y]=='.')
{
db=next.t>9? (next.t-10+'a'):(next.t+'0');
map[next.x][next.y]=db;
que.push(next);
}
show();
}
}

return -1;

}


运行结果为



(10以后用abc表示)

这样就可以输出其路径了

如何将路径输出

还有一个问题要解决 ,就是如何从b返回到a。

可以采用和如队列之前一样的算法,因cur和next分别查找上下左右,是否符合,然后交替cur和next

代码为

void print_path(void)
{
Point cur,next;
int i;
cur=end;
while (!((cur.x==start.x)&&(cur.y==start.y)))
{
for(i=0;i<4;++i)
{
next.x=cur.x+dir[i][0];
next.y=cur.y+dir[i][1];
if(map[next.x][next.y]==map[cur.x][cur.y]-1)
break;
}

cout<<"x= "<<next.x<<"y= "<<next.y<<endl;
cur=next;

}
}


ps为了方便优先级都用int转char,可能打出来很难看

运行结果为



找到了回来的路径

全部测试代码为

#include <iostream>
#include <queue>
#include <algorithm>
#include <fstream>
#include <string>
using namespace std;
/*
int main()
{
priority_queue <int,vector<int>,greater<int>> pq;
pq.push(6);
pq.push(1);
pq.push(3);
pq.push(2);
pq.push(9);
while (!pq.empty())
{
cout<<pq.top()<<" ";
pq.pop();
}
cout<<endl;

}
*/

typedef struct p
{
int x,y,t;
bool operator < (const p &a) const
{
return t>a.t;
}
}Point;

char map[20][20];
Point start;
Point end;
int n,m;
int dir[][2]={{1,0},{-1,0},{0,1},{0,-1}};

void show()
{
cout<<endl;
for(int i=0;i<n;++i)
cout<<map[i]<<endl;
}
int bfs()
{
priority_queue <Point> que;
Point cur,next;
int i;
char db;

map[start.x][start.y]=0;
que.push(start);
show();

while(!que.empty())
{
cur=que.top();
que.pop();
for(i=0;i<4;++i)
{
next.x=cur.x+dir[i][0];
next.y=cur.y+dir[i][1];
next.t=cur.t+1;

if( next.x<0 || next.x>=n || next.y<0 || next.y>=m)
continue;
if(map[next.x][next.y] == '#')
continue;
if(map[next.x][next.y]=='e')
{
map[next.x][next.y]=next.t;
end=next;
return next.t;
}
if(map[next.x][next.y]=='.')
{
//db=next.t>9? (next.t-10+'a'):(next.t+'0');
map[next.x][next.y]=next.t;
que.push(next);
}
show();
}
}

return -1;

}

void print_path(void) { Point cur,next; int i; cur=end; while (!((cur.x==start.x)&&(cur.y==start.y))) { for(i=0;i<4;++i) { next.x=cur.x+dir[i][0]; next.y=cur.y+dir[i][1]; if(map[next.x][next.y]==map[cur.x][cur.y]-1) break; } cout<<"x= "<<next.x<<"y= "<<next.y<<endl; cur=next; } }

int main()
{
freopen("test.txt","r",stdin);
int i,ans;
char* p;

cin>>n>>m;
for (i=0;i<n;++i)
{
scanf("%s",map[i]);
if(p=strchr(map[i],'s'))
{
start.x=i;
start.y=p-map[i];
start.t=0;
}
//printf("%s\n",map[i]);
}
ans=bfs();
cout<<"ans "<<ans<<endl;

show();

print_path();
return 0;

}

test.txt为
7 8
#.#####.
#.s#..e.
#..#....
..#..#.#
#...##..
.#......
........
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: