最短路
最短路
单源最短路:
边权为正:
朴素版 \(dijkstra\)
复杂度 \(O(n^2)\)
思想:
循环 \(n\) 次,每次找到还没有被标记过的 \(dist\) 值最小的节点 \(u\)
用 \(u\) 来更新其他节点的 \(dist\) :
\(dist[j]=\min(dist[j],dist[u]+g[u][j])\)
标记节点 \(u\)
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;//初始化
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
st[t]=true;
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
堆优化版 \(dijkstra\)
复杂度 \(O(m \log n)\)
将朴素版中“找到还没有被标记过的 \(dist\) 值最小的节点 \(u\) ”的操作堆优化
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII> > heap;//小根堆
heap.push({0,1});
while(heap.size())
{
PII t=heap.top();
heap.pop();
int ver=t.second,distance=t.first;
if(st[ver]) continue;
st[ver]=true;//打上标记,防止重复入队
for(int i=h[ver];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[ver]+w[i])
{
dist[j]=dist[ver]+w[i];
heap.push({dist[j],j});//如果被更新就加入队列中
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
边权为负:
\(dijkstra\) 不能处理负边权
spfa
spfa求最短路
复杂度: 最坏 \(O(VE)\)
思路:
保证队列中无重复元素
每次用被更新过的节点去更新其他节点
int spfa()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int>q;
q.push(1);
st[1]=true;//入队
while(q.size()){
int u=q.front();
q.pop();
st[u]=false;//出队,删除标记
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(dist[j]>dist[u]+w[i]){
dist[j]=dist[u]+w[i];
if(!st[j]){//如果队列中没有该节点,且该节点被更新过,则入队
q.push(j);
st[j]=true;
}
}
}
}
return dist[n];
}
spfa 判断负环
\(cnt[j]\) : 从 \(1\) 到 \(j\) 的最短路经过的点数(除节点 \(1\) 外)
如果 \(cnt[j] >=n\) : 最短路一定至少重复经过一个点,说明存在环。因为其满足最短路,所以环一定是负环
如果
bool spfa()
{
queue<int>q;
for(int i=1;i<=n;i++){
q.push(i);
st[i]=true;// 图可能不连通
}
while(q.size()){
int u=q.front();
q.pop();
st[u]=false;
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(dist[j]>dist[u]+w[i]){
dist[j]=dist[u]+w[i];
cnt[j]=cnt[u]+1;//转移cnt
if(cnt[j]>=n)return true;
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
有边数限制的最短路(bellman_ford)
从 \(1\) 号点到 \(n\) 号点的最多经过 \(k\) 条边的最短距离
可能存在负环
复杂度: \(O(km)\)
循环 \(k\) 次,每次用每条边更新 \(dist\)
void bellman_ford()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++)
{
memcpy(backup,dist,sizeof dist);//要用前一转态更新,先保存前一转态(避免单次重复更新)
for(int j=0;j<m;j++)
{
int a=edge[j].a,b=edge[j].b,w=edge[j].w;
if(dist[b]>backup[a]+w) dist[b]=backup[a]+w;
}
}
}
多源最短路floyd
复杂度 \(O(n^3)\)
用二维数组存图,注意重边的影响。
void floyd()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}

浙公网安备 33010602011771号