dijkstra算法问题总结

1.问题1

如果给出的节点数很大但是并非是按照顺序增长怎么办?
比如在在如下的题中:PAT 1131 Subway Map
这里的节点就是 0000-9999 中的一个数,但是题中的数字并不是连续的递增序列,这就导致我们需要使用maxn = 10000作为遍历上限,而这样得到的时间就是N^2,导致耗时很久。

#include<cstdio>
#include<iostream>
#include<map>
#include<set>
#define maxn 10005
#define INF 0x3fffffff

using namespace std;
int N = 10000;
int G[maxn][maxn];//地铁图信息 
int path[maxn];//路径信息
int d[maxn];
int isVis[maxn];//是否访问过 
map<int,int> bts;//big to small 
map<int,int> stb;//small to big

void init(){
	fill(d,d+maxn,INF);
	fill(G[0],G[0]+maxn*maxn,INF);//表明两边之间没有联系 
	for(int i = 0;i < maxn;i++){
		path[i] = i;
	}
	fill(isVis,isVis+maxn,0);//未访问过 
}

void dijkstra(int s){
	int u; 
	int i;
	int min = INF;
	for (i = 0;i < maxn;i++){
		if(min > d[i] && isVis[i] == 0) {//最短且未访问 
			min = d[i];
			u = i;
		}
	}
	isVis[u] = 1;//修改距离 
	for(i = 0;i< maxn;i++){
		if(isVis[i] == 0 && d[i] > d[u] + G[i][u]){//如果当前的这个距离是最小的 
			d[i] =  d[u] + G[i][u];//更新距离 
			path[i] = u;//更新前驱 
		}
	}
} 

int main(){
	init();
	cin >> N;
	int i,j;
	int staNum;//车站数
	int curSta;//当前车站 
	int preSta;//上一个车站 	
	for(i = 0;i< N;i++){
		cin >> staNum;				
		cin >> curSta;//输入当前车站
		preSta = curSta; 		
		for(j = 1;j< staNum;j++){
			cin >> curSta;
			G[curSta][preSta] = 1;//表明有边
			G[preSta][curSta] = 1;			
			preSta = curSta; 
		}
	}
	
	//开始求 单源最短路径 
	int sour,des;
	cin >> sour >>des;
	d[sour] = 0; 
	for(i = 0;i< maxn;i++){	
		dijkstra(sour); //源点sour到其它各个节点的距离 	
	}
	cout << d[des]; 
}
/*
4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
6666 2001
*/ 

测试用例得到的图如下:
dijkstra算法总结
上面这个程序的执行结果如下:
dijkstra算法总结
可以看到耗时 3.598s才计算出来结果,这显然是不合理的,那么我们可以使用什么方法简化呢?
使用 map** 建一个节点到序号的双向映射 **可以很好地解决这个问题。但问题是:map对处理一些小数量的数据很有优势,但是对于处理大数据的情况下,这个映射的建立就是多余操作了,比如说现在就有10000个节点,那么这么建立映射就是浪费的操作了。

修改后的代码如下所示:

#include<cstdio>
#include<iostream>
#include<map>
#include<set>
#define maxn 10005
#define INF 0x3fffffff

using namespace std;
int N = 10000;
int G[maxn][maxn];//地铁图信息 
int path[maxn];//路径信息
int d[maxn];
int isVis[maxn];//是否访问过 
map<int,int> bts;//big to small 
map<int,int> stb;//small to big
int count = 0;//维持图中总的结点数 

void init(){
	fill(d,d+maxn,INF);
	for(int i = 0;i < maxn;i++){
		path[i] = i;
	}
	fill(isVis,isVis+maxn,0);//未访问过 
}

void dijkstra(int s){
	int u; 
	int i;
	int min = INF;
	for (i = 0;i < count;i++){
		if(min > d[i] && isVis[i] == 0) {//最短且未访问 
			min = d[i];
			u = i;
		}
	}
	isVis[u] = 1;//修改距离 
	for(i = 0;i< count; i++){
		if(isVis[i] == 0 && d[i] > d[u] + G[i][u]){//如果当前的这个距离是最小的 
			d[i] =  d[u] + G[i][u];//更新距离 
			path[i] = u;//更新前驱 
		}
	}
} 

int main(){
	init();
	fill(G[0],G[0]+maxn*maxn,INF);//表明两边之间没有联系 
	cin >> N;
	int i,j;
	int staNum;//车站数
	int curSta;//当前车站 
	int preSta;//上一个车站 	
	for(i = 0;i< N;i++){
		cin >> staNum;				
		cin >> curSta;//输入当前车站		
		preSta = curSta; 
		if(bts.find(curSta) == bts.end()){//如果找不到 
			bts[curSta] = count ;
			stb[count] = curSta;
			count++;
		}
		for(j = 1;j< staNum;j++){
			cin >> curSta;
			if(bts.find(curSta) == bts.end()){//如果找不到 
				bts[curSta] = count ;
				stb[count] = curSta;
				count++;
			}
			G[bts[curSta]][bts[preSta]] = 1;//表明有边
			G[bts[preSta]][bts[curSta]] = 1;			
			preSta = curSta; 			
		}
	}	
	
	//开始求 单源最短路径 
	int queNum;
	int sour,des;
	cin >> queNum;
	for(i = 0;i< queNum;i++){
		init();//重新初始化				
		cin >> sour >> des;	
		d[bts[sour]] = 0; 
		for(j = 0;j< count;j++){	
			dijkstra(bts[sour]); //求sour 到其它各个节点的距离 	
		}
		cout << d[bts[des]] <<"\n"; 
	}
}

测试用例如下:

4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
4
1001 3001
3011 3013
6666 2001
2004 3001

得到的执行结果如下:
dijkstra算法总结

相关文章:

猜你喜欢
  • 2021-09-19
  • 2022-12-23
  • 2021-09-16
  • 2022-01-20
  • 2021-11-28
  • 2021-07-28
相关资源
相似解决方案