背景
上学期的通信网理论基础课上,双向dijkstra是一个选作的研究性课题,当时没做,这个假期有时间了准备写一个。写着写着发现既然是双向,两边同时运行肯定比来回切换的“伪同时”要更快,反正现在的CPU基本都是二核以上的。
开始想用fork()函数来着,后来发现windows下没有这玩意,又找到了CreateThread,结果成功实现。
大致思想
依旧是在网格地图中的寻路,由于要在两个方向上进行寻路,于是需要维护两个Priority_queue,而主体dijkstra不需要写两个,只要在传入参数中加一个方向标识即可。在判断寻路完毕上,我最终决定在visited上下手,即将原本的二元0-1 visited变为三元visited——0,正向访问完毕1,反向访问完毕1,当某方向探测到另一方向上的访问完毕标识时,即结束dijkstra,输出结果。与之相应的,控制dijkstra持续运行的不再是while而是递归。
实例代码
#include <iostream>
#include <fstream>
#include <queue>
#include <vector>
#include <windows.h>
#define unit 10
#define INF 10000
#define forward 1
#define back 0
#define f_visit 6 //正方向访问标识
#define b_visit 7 //逆方向访问标识
using namespace std;
class TPoint{
public:
int x;
int y;
};
const int line=23;
const int column=61;
int dis_f[line][column];
int dis_r[line][column];
int visited[line][column];
int map[line][column];
TPoint pre[line][column];
struct cmp_f{
bool operator()(TPoint a,TPoint b){
return dis_f[a.x][a.y]>dis_f[b.x][b.y];
}
};
struct cmp_r{
bool operator()(TPoint a,TPoint b){
return dis_r[a.x][a.y]>dis_r[b.x][b.y];
}
};
priority_queue<TPoint,vector<TPoint>,cmp_f> Q_front;
priority_queue<TPoint,vector<TPoint>,cmp_r> Q_back;
int Distance_Judgement(int ori_x, int ori_y, int next_x, int next_y){
if(ori_x==next_x && abs(ori_y-next_y)==1)
return unit;
if(ori_y==next_y && abs(ori_x-next_x)==1)
return unit;
if(abs(ori_x-next_x)==1 && abs(ori_y-next_y)==1)
return 1.4*unit;//对角线走法路经长为根号2倍单位
}
bool Meeting_Detector(TPoint v, bool direction){//原理是两个方向上采用不同的visited记号,当某方向上探测到另一种visited记号时即停止探测返回结果
switch(direction){
case forward:{
if(visited[v.x][v.y]==b_visit){
//cout<< "正方向探测到终结"<< endl;
return TRUE;
}
else
return FALSE;
break;
}
case back:{
if(visited[v.x][v.y]==f_visit){
//cout<< "逆方向探测到终结"<< endl;
return TRUE;
}
else
return FALSE;
break;
}
}
}
void Initia(TPoint ori, TPoint des){
for(int i=0; i<line; i++)
for(int j=0; j<column; j++){//二维数组不能用={0}这种简单方式初始化
pre[i][j].x = -1;
dis_f[i][j] = INF;
dis_r[i][j] = INF;
visited[i][j] = 0;
}
ifstream fin("map.txt", ios::in);
while(fin.good())
for(int i=0; i<line; i++)
for(int j=0; j<column; j++)
fin>> map[i][j];
dis_f[ori.x][ori.y] = 0;
dis_r[des.x][des.y] = 0;
}
int Bi_Dijkstra(TPoint u, bool direction){
int i, j;
TPoint temp, next;
if(Meeting_Detector(u, direction)){
if(direction==forward){
Sleep(5);
cout<< "最短路径前半段长为"<< dis_f[u.x][u.y]<< "后半段长为"<< dis_r[u.x][u.y]<< endl;
cout<< "总长为"<< dis_f[u.x][u.y]+dis_r[u.x][u.y]<< endl;
cout<< "正向探测相遇点为"<< u.x<< ","<< u.y<< endl;
}
if(direction==back){
Sleep(15);
cout<< "最短路径前半段长为"<< dis_f[u.x][u.y]<< "后半段长为"<< dis_r[u.x][u.y]<< endl;
cout<< "总长为"<< dis_f[u.x][u.y]+dis_r[u.x][u.y]<< endl;
cout<< "逆向探测相遇点为"<< u.x<< ","<< u.y<< endl;
}
/*搜索区域染色输出
ofstream os("map_mirrir.txt");
if (os)
{
for (int i=0;i<line;i++)
{
for (int j=0;j<column;j++)
os<< map[i][j]<<" ";
os<<endl;
}
os<<endl;os<<endl;os<<endl;
}
else
cerr<<"error"<<endl;
*/
Sleep(20); //执行快的那边等一会执行慢的,让它输出完
return 0;
}
if(direction==forward){
visited[u.x][u.y] = f_visit;
//map[u.x][u.y] = f_visit; //染色
}
if(direction==back){
visited[u.x][u.y] = b_visit;
//map[u.x][u.y] = b_visit; //染色
}
for(i=u.x-1; i<=u.x+1; i++){
for(j=u.y-1; j<=u.y+1; j++){
if(map[i][j]==0 || (i<0||i>=line) || (j<0||j>=column)){
continue;
}
else if(map[i][j]!=0){
int pace = Distance_Judgement(u.x, u.y, i, j);
if(direction==forward && visited[i][j]!=f_visit){
if(dis_f[i][j]==INF || dis_f[u.x][u.y]+pace < dis_f[i][j]){
dis_f[i][j] = dis_f[u.x][u.y]+pace;
pre[i][j].x = u.x; pre[i][j].y = u.y;
temp.x = i; temp.y = j;
//visited[i][j] = f_visit; //这个在我上一个常规dijkstra算法里没有注释掉,想了想其实应该注释掉的,虽然对结果没影响
Q_front.push(temp);
}
}
if(direction==back && visited[i][j]!=b_visit){
if(dis_r[i][j]==INF || dis_r[u.x][u.y]+pace < dis_r[i][j]){
dis_r[i][j] = dis_r[u.x][u.y]+pace;
pre[i][j].x = u.x; pre[i][j].y = u.y;
temp.x = i; temp.y = j;
//visited[i][j] = b_visit; //同上
Q_back.push(temp);
}
}
}
}
}
switch(direction){
case forward:{
next = Q_front.top();
Q_front.pop();
Sleep(1); //因为是并发执行,而且速度很快,不缓冲一下程序就报错,而且也看不出并发效果了
Bi_Dijkstra(next, forward);
break;
}
case back:{
next = Q_back.top();
Q_back.pop();
Sleep(1);//缓冲
Bi_Dijkstra(next, back);
break;
}
}
}
DWORD WINAPI ThreadProc(LPVOID lpParameter){ //子线程
TPoint ori, temp1;
ori.x = 0; ori.y = 0;
Q_front.push(ori);
temp1 = Q_front.top();
Q_front.pop();
Bi_Dijkstra(temp1, forward);
}
int main(){
TPoint ori, des, temp2;
ori.x = 0; ori.y = 0;
des.x = 22; des.y = 60;
Initia(ori, des);
HANDLE thread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);//子线程创建
CloseHandle(thread);//结束子线程
Q_back.push(des);//主线程部分
temp2 = Q_back.top();
Q_back.pop();
Bi_Dijkstra(temp2, back);
return 0;
}