今天考试的时候遇到了一道题需要路径计数,然而蒟蒻从来没有做过,所以在考场上真的一脸懵逼。然后出题人NaVi_Awson说明天考试还会卡SPFA,吓得我赶紧又来学一波堆优化的Dijkstra(之前只会SPFA。。。
堆优化Dijkstra
其实Dijkstra的思想很简单。SPFA是以边为基础的最短路松弛,那么Dijkstra恰好相反,是以点为基础的最短路松弛。划分两个点的集合,一个是已经松弛的点集合,一个是未松弛的点集合,每次从已松弛的点集合中找当前路径最小的点来松弛与它相连的未松弛的点。但是如果是不加优化的Dijkstra复杂度是$O(n^2)$的,肯定会T,所以这里引入堆优化,复杂度可以降到$O((n+m)\log n)$。
路径计数
路径计数其实也很简单,在做最短路的时候多引入一个数组,表示当前从远点到达该点的最短路径条数。松弛的时候如果发现该点当前的最短路长度和它被松弛得到的长度相等,那么说明到达它的最短路径条数又增加了,那么就把该点的路径条数加上松弛点的路径条数;如果该点的最短路长度小于被松弛的长度,那么就更新最短路,同时更新路径条数,令该点的最短路径条数等于松弛点的路径条数。
最后再放上自己打的堆优化Dijkstra最短路+路径计数代码。
Code:
//It is made by HolseLee on 8th Aug 2018 //Dijkstra #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<iomanip> #include<algorithm> #include<queue> using namespace std; const int mod=1e5+3; const int N=1e6+7; int n,m,sta,ed,dis[N],p[N],head[N],size; bool vis[N]; struct Node{ int to,val,next; }edge[N<<2]; struct Cmp{ bool operator()(int a,int b){ return dis[a]>dis[b]; } }; priority_queue<int,vector<int>,Cmp> t; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline void add(int x,int y,int z) { edge[++size].to=y; edge[size].val=z; edge[size].next=head[x]; head[x]=size; } void dijkstra() { memset(vis,false,sizeof(false)); memset(dis,0x7f,sizeof(dis)); t.push(sta); dis[sta]=0;p[sta]=1; int x,y,z; while(!t.empty()){ x=t.top();t.pop(); if(vis[x])continue; vis[x]=true; for(int i=head[x];i!=-1;i=edge[i].next){ y=edge[i].to; if(dis[y]==dis[x]+edge[i].val) p[y]=(p[x]+p[y])%mod; else if(dis[y]>dis[x]+edge[i].val){ dis[y]=dis[x]+edge[i].val; p[y]=p[x]; t.push(y); } } } } int main() { n=read();m=read(); sta=read();ed=read(); memset(head,-1,sizeof(head)); int x,y,z; for(int i=1;i<=m;++i){ x=read();y=read();z=read(); if(x==y)continue; add(x,y,z);add(y,x,z); } dijkstra(); printf("%d %d",dis[ed],p[ed]); return 0; }