Dij:一种贪心思想,每次找未被标记且离起点最近的点标记并用来松弛其他路径
注:不适用有负权边的图
复杂度:mlogm(严谨:(n+m)logm)
priority_queue<PII,vector<PII>,greater<PII>>q;//小根堆维护最小值
void dij()
{
memset(dis,0x3f,sizeof(dis));
q.push({0,s});
dis[s]=0;
while(q.size())
{
auto p=q.top();
q.pop();
if(vis[p.second])continue;
vis[p.second]=1;
for(int i=head[p.second];i;i=ed[i].next)
{
if(vis[ed[i].x])continue;
if(dis[ed[i].x]>ed[i].w+p.first)
{
dis[ed[i].x]=ed[i].w+p.first;
q.push({dis[ed[i].x],ed[i].x});
}
}
}
}
Bellman-Ford算法:枚举所有的点,能松弛就松弛,直到所有点不能松弛
注:不能有负环
while(b)
{
b=false;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(d[j]+map[j][i]<d[i])
{
d[i]=d[j]+map[j][i];
b=true;
}
}
复杂度大概 n^3,可用队列优化成SPFA
SPFA:维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点s。用一个布尔数组记录每个点是否在队列中。(若一个点入队次数超过n则有负环)
void spfa(int s)
{
memset(dis,0x3f,sizeof(dis));
queue<int>q;
dis[s]=0;
vis[s]=1;
q.push(s);
while(q.size())
{
int p=q.front();
q.pop();
vis[p]=0;
for(int i=head[p];i;i=ed[i].next)
{
int y=ed[i].x;
if(dis[y]>dis[p]+ed[i].w)
{
dis[y]=dis[p]+ed[i].w;
if(!vis[y])
{
q.push(y);
vis[y]=1;
}
}
}
}
}
复杂度:km,k是每个点平均入队次数,一般为10左右,但容易被卡,只适用有负权边的图。
Floyd算法:动态规划思想,枚举中转点。(多源最短路)(未优化)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if((i!=j)&&(j!=k)&&(k!=i))
{
if(f[i][k]+f[k][j]<=f[i][j])
f[i][j]=f[i][k]+f[k][j];
}
复杂度:n^3