Dijksta算法中,如果我们采用的是邻接矩阵来存的,第一点浪费的空间比较多,第二点我们知道算法的时间复杂度在O(n*n),这样的算法可以说并不是很好,所以我们考虑优化它首先我们可以优化存储结构,采用邻接表来存储,其次我们可以用优先队列来排序大小,其时间复杂度大大降低。
需要注意的是pair是按照第一个元素的大小排序,如果相同才按照第二个,所以我们要把d[i]包装在第一个元素上。
vector实现邻接表+优先队列 (假设边一开始是字符型的,这么假设是为了加点难度)
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#include<string>
#include<cstring>
#define inf 0x7fffffff
using namespace std;
const int MAXN = 205;
int dis[MAXN];
int n; //顶点数目
int m; //边的条数
string src,ed; //src是源点,ed是目标点
typedef pair<int,int> pii;
struct edge //建立边的结构体
{
int u;
int v;
int w;
edge(int a,int b,int c) //构造函数,建立结构体的时候直接调用这个函数,方便了一个一个赋值。
{
u = a;
v = b;
w = c;
}
};
map<string,int>M; //利用map关联容器为字符串型的边进行标号
vector<edge> G[MAXN];//邻接表
void init()
{
M.clear();//每次循环要对map清空
int cnt=0;
cin>>n>>m;
string u,v;//字符型顶点
int w;//权值
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
if(!M.count(u))
M.insert(make_pair(u,++cnt)); //make_pair不仅可以用在pair<>的插入中也可用在map,vector等容器中
if(!M.count(v))
M.insert(make_pair(v,++cnt)); //利用map关联容器为字符串型的边进行标号,1,2,3...注意A不一定是1号边,最先读入的才是1号边。
edge E1(M[u],M[v],w); //建立无向边,调用构造函数,简洁。
edge E2(M[v],M[u],w);
G[M[u]].push_back(E1); //建立邻接表
G[M[v]].push_back(E2);
}
cin>>src>>ed;
for(int i =1;i<=n;i++) dis[i] = (i == M[src] ? 0 : inf);//初始化dis
}
void dijktra()
{
priority_queue<pii,vector<pii>,greater<pii> > que;
que.push(make_pair(0,M[src]));//将起点插入队列,pair默认是优先处理first元素,故插入优先队列先弹出队列的优先级是依据dis[]大小
while(!que.empty())
{
pii tmp = que.top();
que.pop();
int x = tmp.second;
if(tmp.first != dis[x]) continue; //可避免结点的重复拓展,这里每一个元素出队都相当于处理一次已标号结点,如果出队的这个元素,
//他带的dis,和当前的dis不相同,证明这个结点是被处理过的,这样就不需用开一个数组来标记哪些点被处理过了。
for(int i = 0;i < G[x].size();i++)
{
int y = G[x][i].v;
int w = G[x][i].w;
if(dis[y] > dis[x] + w)
{
dis[y] = dis[x] + w;
que.push(make_pair(dis[y],y));
}
}
}
}
int main()
{
// freopen("1.in","r",stdin);
int _;
cin>>_;
while(_--){
init();
dijktra();
if(dis[M[ed]]==inf) cout<<"-1"<<endl;
else cout<<dis[M[ed]]<<endl;
/*输出源点依次到ABC的距离,不能直接把dis按顺序输出,否则可读性差,因为A点不一定是1号点,1号点是最先读入的点,所以利用map以对点A,B,C依排序,*/
for(map<string,int>::iterator it=M.begin();it!=M.end();it++) printf("%d ",dis[it->second]);
putchar('\n');
}
return 0;
}
输入数据:
1
6 9
D E 13
B C 9
A B 1
A C 12
B D 3
E C 5
D C 4
D F 15
E F 4
A F
输出数据:
6 9
D E 13
B C 9
A B 1
A C 12
B D 3
E C 5
D C 4
D F 15
E F 4
A F
输出数据:
17
0 1 8 4 13 17
0 1 8 4 13 17
数组实现邻接表+优先队列
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x7fffffff
using namespace std;
const int MAXN = 205;
int dis[MAXN];
int u[MAXN],v[MAXN],w[MAXN];
int first[MAXN],next[MAXN];
int n,m;
int src,ed;
typedef pair<int,int> pii;
void init()
{
scanf("%d%d",&n,&m);
memset(first,-1,sizeof(first));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u[i],&v[i],&w[i]);
next[i]=first[u[i]];
first[u[i] ]=i;
u[i+m]=v[i],v[i+m]=u[i],w[i+m]=w[i]; //无向边,所以加一条反向边
next[i+m]=first[u[i+m] ];
first[u[i+m] ]=i+m;
}
cin>>src>>ed;
for(int i=1;i<=n;i++) dis[i]= (i==src? 0:inf);//不要把dis[i]初始化成源点到各点的直接距离,否则会有点没入队。
}
void dijkstra()
{
priority_queue<pii,vector<pii>,greater<pii> >que;
que.push(pii(0,src));
while(!que.empty()){
pii tmp=que.top();
que.pop();
int x = tmp.second;
if(tmp.first!=dis[x]) continue; //如果出队的这个元素,他带的dis,和当前的dis不相同,证明这个结点是被处理过的已确定了最短路,
for(int e=first[x];e!=-1;e=next[e]){ //这种数组式邻接表的遍历
if(dis[v[e]]>dis[x]+w[e]){
dis[v[e] ]=dis[x]+w[e];
que.push(pii(dis[v[e] ],v[e]));
}
}
}
}
int main()
{
// freopen("1.in","r",stdin);
int _;
scanf("%d",&_);
while(_--){
init();
dijkstra();
if(dis[ed]==inf) cout<<"-1"<<endl;
else cout<<dis[ed]<<endl;
for(int i=1;i<=n;i++) printf("%d ",dis[i]);//输出dist数组各个值
putchar('\n');
}
}
测试数据:
1
6 9
1 2 1
1 3 12
2 3 9
2 4 3
5 3 5
4 3 4
4 5 13
4 6 15
5 6 4
1 6
6 9
1 2 1
1 3 12
2 3 9
2 4 3
5 3 5
4 3 4
4 5 13
4 6 15
5 6 4
1 6
输出:
17
0 1 8 4 13 17
0 1 8 4 13 17