最短路(基础篇)
全源最短路模板
Floyd
我们设 \(f_{i,j}\) 表示 \(i\) 到 \(j\) 的最短路,于是我们枚举中转点 \(k\),就有 \(f_{i,j}\leftarrow\min(f_{i,j},f_{i,k}+f_{k,j})\)。
于是简单枚举一下进行转移即可,时间复杂度 \(O(n^3)\)。
代码:
for(k=1;k<=n;k++){
for(x=1;x<=n;x++){
for(y=1;y<=n;y++){
f[x][y]=min(f[x][y],f[x][k]+f[k][y]);
}
}
}
单源最短路模板
单源最短路径
我们考虑一条边 \(u\rightarrow v\),我们尝试进行松弛:\(dis_v\leftarrow\min(dis_v,dis_u+w(u,v))\)。
于是 \(Bellman-Ford(SPFA)\) 算法所做的,就是尝试不断对每条边进行松弛。每进行一轮循环,就将图上所有的边松弛一次,于是复杂度为 \(O(nm)\)。
注意:如果一条边被松弛了 \(n\) 次及以上,说明图中存在负环。
因为 \(Bellman-Ford\) 严格不优于,所以我们给出 \(SPFA\) 的实现(只是多了一个队列优化):
int spfa(int S,int T){
int hh=0,tt=1;
memset(d,0x3f,sizeof d);
q[0]=S;st[S]=1;d[S]=0;
while(hh!=tt){
int t=q[hh++];
if(hh==N)hh=0;
st[t]=0;
for(int i=h[t];~i;i=ne[i]){
int j=e[i];
if(d[j]>d[t]+w[i]){
d[j]=d[t]+w[i];
if(!st[j]){
q[tt++]=j;
if(tt==N)tt=0;
st[j]=1;
}
}
}
}
return d[T]>=d[0]/2?d[0]:d[T];
}
单源最短路径(强化版)
上述算法会被卡掉,所以我们考虑一种新的最短路算法 \(dijkstra\)。
我们考虑现在有两个集合,一个是已经确定最短路的 \(S\),剩下的则是 \(T\).
初始化 \(dis_s=0\),然后从 \(T\) 中选取一个最短路长度最小的点,把他移动到 \(S\) 中,然后对所有新移动到 \(S\) 中的点的出边执行松弛操作。
堆优化代码:
void dijkstra(int s){
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
q.push({0,s});
while(!q.empty()){
int u=q.top().u;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto ed:e[u]){
int v=ed.v,w=ed.w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
}