//使用邻接矩阵并记录路径的Dijkstra
#include<vector> #include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int MAXV = 1000; const int INF = 0xFFFFFFF; int n,m,G[MAXV][MAXV]; int d[MAXV]; int pre[MAXV]; bool vis[MAXV]={false}; void Dijkstra(int s) { for(int i=0;i<n;i++) d[i]=INF,pre[i]=i; //fill(d,d+MAXV,INF); d[s]=0; for(int i=0;i<n;i++)//必须把每个定点标记为访问过 { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; //说明无法到达其他的定点,也就是其他的点到s的距离为d[i]=INF,此时直接返回即可 vis[u]=true; //访问这一点 for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=0 && d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; pre[v]=u; } } } } void print(int u) { if(pre[u]!=u) print(pre[u]); printf("%d ",u); } int main(void) { int s,u,v,w; cin>>n>>m>>s; for(int i=0;i<m;i++) { cin >> u >> v >> w; G[u][v]=w; } Dijkstra(s); for(int i=0;i<n;i++) printf("%d ",d[i]); printf(" "); for(int i=0;i<n;i++) { print(i); printf(" "); } return 0; }
测试数据:
6 8 0 0 1 1 0 3 4 0 4 4 1 3 2 2 5 1 3 2 2 3 4 3 4 5 3
第一行是n,m,s;
然后是m行起点,终点,权重
//使用邻接表的Dijkstra,输出路径
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> using namespace std; const int MAXV = 1000; const int INF = 0xFFFFFFF; int n,pre[MAXV],d[MAXV],vis[MAXV]; struct Node{ int v,dis; Node(int _v,int _dis){ v=_v; dis=_dis; } }; vector<Node>Adj[MAXV]; void print(int u) { if(pre[u]!=u) print(pre[u]); printf("%d ",u); } void Dijkstra(int s) { for(int i=0;i<n;i++) d[i]=INF,pre[i]=i; d[s]=0; for(int i=0;i<n;i++) { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; vis[u]=true; for(int j=0;j<Adj[u].size();j++) { int v=Adj[u][j].v; if(vis[v]==false && d[u]+Adj[u][j].dis<d[v]) { pre[v]=u; d[v]=d[u]+Adj[u][j].dis; } } } } int main(void) { int u,v,w,m,s; cin>>n>>m>>s; for(int i=0;i<m;i++) { cin >> u >> v >> w; Adj[u].push_back(Node(v,w)); } Dijkstra(s); for(int i=0;i<n;i++) printf("%d ",d[i]); printf(" "); for(int i=0;i<n;i++) { printf("路径:%d:",i+1); print(i); printf(" "); } return 0; }
输出路径的函数使用如下更加方便:
void DFS(int s,int v) { if(v==s) { printf("%d ",v); return ; } DFS(S,pre[v]); printf("%d ",v); }
Dijkstra算法在有两条或者两条以上的最短路径可以到达同一个地方的时候,一般会给出第二个标尺。
1.边的权值
2.点的权值
3.最短的路径的个数
//边权的使用,邻接矩阵的情况,注意初始化只有c[v]=0,其他为INF
//对于邻接表,可以使用邻接表存放一个顶点可以到达的其他点,然后将边权仍然放到一个矩阵中,方便查询
for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; c[v]=c[u]+cost[u][v]; } else if(d[u]+G[u][v]==d[v] && c[u]+cost[u][v]<c[v]) { c[v]=c[u]+cost[u][v]; } } }
//增加点权的情况,初始化时候只有w[s]=weight[s],其他为INF
for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; w[v]=w[u]+weight[v]; } else if(d[u]+G[u][v]==d[v] && w[v]<w[u]+weight[v]) { w[v]=w[u]+weight[v] } } }
//计算路径:初始化只有num[s]=1,其他为INF;
for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; num[v]=num[u]; } else if(d[u]+G[u][v]==d[v]) { num[v]+=num[u]; } } }
举例子:
给出N个城市,M条无向边。每个城市都有一定的救援小组,所有边的权值已知(此处的边权不是路径长度,是第二尺度)。给出起点和终点,求从起点到终点的最短路径条数以及路径上的救援小组之和。如果有多条最短路径,输出救援小组数目之和最大的。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXV = 1000; const int INF = 0xFFFFFFF; //n为定点数,m为边数,st起点,ed终点 //G为邻接矩阵,weight为点权 //d[]记录最短路径长度,w[]记录最大点权之和,num[]记录最短路径数量 int n,m,st,ed,G[MAXV][MAXV],weight[MAXV]; int d[MAXV],w[MAXV],num[MAXV]; bool vis[MAXV]={false}; void Dijkstra(int s) { fill(d,d+MAXV,INF); memset(num,0,sizeof(num)); memset(w,0,sizeof(w)); d[s]=0; w[s]=weight[s]; num[s]=1; for(int i=0;i<n;i++) { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; vis[u]=true; for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; w[v]=w[u]+weight[v]; num[v]=num[u]; } else if(d[u]+G[u][v]==d[v]) { if(w[u]+weight[v]>w[v]) w[v]=w[u]+weight[v]; num[v]+=num[u]; //写在if的外面 } } } } } int main(void) { scanf("%d%d%d%d",&n,&m,&st,&ed); for(int i=0;i<n;i++) scanf("%d",&weight[i]); int u,v; fill(G[0],G[0]+MAXV*MAXV,INF); for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); scanf("%d",&G[u][v]); G[v][u]=G[u][v]; } Dijkstra(st); //for(int i=0;i<n;i++) printf("%d %d %d ",d[i],w[i],num[i]); printf("%d %d ",num[ed],w[ed]); return 0; } /* 5 6 0 2 1 2 1 5 3 0 1 1 0 2 2 0 3 1 1 2 1 2 4 1 3 4 1 答案:2 4 */
Dijkstra+DFS
//Dijkstra部分
void Dijkstra(int s) { fill(d,d+MAXV,INF); d[s]=0; for(int i=0;i<n;i++)//必须把每个定点标记为访问过 { int u=-1,MIN=INF; for(int j=0;j<n;j++) { if(vis[j]==false && d[j]<MIN) { u=j; MIN=d[j]; } } if(u==-1) return ; //说明无法到达其他的定点,也就是其他的点到s的距离为d[i]=INF,此时直接返回即可 vis[u]=true; //访问这一点 for(int v=0;v<n;v++) { if(vis[v]==false && G[u][v]!=INF) { if(d[u]+G[u][v]<d[v]) { d[v]=d[u]+G[u][v]; pre[v].clear(); pre[v].push_back(u); } else if(d[u]+G[u][v]==d[v]) { pre[v].push_bakc(u); } } } } }
//DFS部分
int optvalue; vector<int>pre[MAXV]; vector<int>path,tempPath; void DFS(int v) { if(v==st) { tempPath.push_back(v); int value; 计算路径tempPath上的value的值:累加边权或者点权的和 if(value 优于 optvalue) { optvalue=value; path=tempPath; } tempPath.pop_back(); } tempPath.push_back(v); for(int i=0;i<pre[v].size();i++) { DFS(pre[u][i]); } tempPath.pop_back(v); }
总结:裸的迪杰斯特拉使用邻接表计算,带有第二尺度的使用Dijkstra的邻接矩阵的方法计算,复杂的第二尺度,使用Dijkstra+DFS计算。