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
*/
测试用例得到的图如下:
上面这个程序的执行结果如下:
可以看到耗时 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
得到的执行结果如下: