最短路的解法:
Dijkstra:堆优化后复杂度O(NlogN),快但不能有负权
Floyd:插点法,经典DP,三重循环,能解决负权问题
Bellman-Ford:复杂度O(MN),能检查负环
SPFA:Bellman-Ford队列优化
Dijkstra堆优化,每次从最近点出发进行松弛操作
struct Edge{
int to,dis,next;
}edge[MANX_edge];
//链式前向星结构体
struct Node{
int id, dis;
bool operator < (const &a) const{return a.dis<dis};
}node[MAXN];
//节点信息
void Dijsktra()
{
priority_queue<node> q;
q.push(node{s,0});
for(int i=0;i<=n;i++) dist[i]=INF;
dist[s]=0;
while(!q.empty())
{
node a=q.top(); q.pop();
int now=a.id;
if(vis[now]) continue;
vis[now]=1;
for(int i=head[now];i;i=edge[i].next)
{
int j=edge[i].to;
if(dist[j]>dist[now]+edge[i].dis)
{
dist[j]=dist[i]+edge[i].dis;
q.push(node{j,dist[j]});
}
}
}
}
Floyd:d[i][j]记录i到j 的最短距离,三重循环,首层从1到n 代表每次加入一个点作为中间点更新
d[i][j],内两层循环遍历起点i , 终点j 。
思想:从i 到j 的只经过前k个点的最短路径
特点:简单、粗暴、易于实现、可以解决负权
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
Bellman-Ford:用边更新最短路径,两层循环,外层n-1次,内层用所有边进行更新
特点:O(nm),简洁,可以解决负权和负环问题
注意,这里路径存储用编号查询,可以查到起点,终点和长度,和其他的路径存储方式不一样
for(k=1;k<=n-1;k++)
for(i=1;i<=m;i++)
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i]
flag=0;
for(i=1;i<=m;i++)
if(dis[v[i]]>dis[u[i]]+w[i]) flag=1;
if(flag==1) ...//有负权的情况
SPFA:对贝尔曼福德的优化,只查可能更新的点
只有从刚更新过的点出发的边才可能更新其他点的dist
void spaf(int u)
{
q.push(u); vis[u]=1; //进堆
while(!q.empty())
{
int x=q.front();
q.pop(); vis[x]=0; //取出
for(int i=first[x];i;i=next[i])
{
int y=v[i];
if(dist[x]+w[i]<dist[y])
{
dist[y]=dist[x]+w[i];
if(!vis[y]) vis[y]=1, q.push(y); //不在堆里就进堆,堆里的都是要更新的
}
}
}
}
总结:dijkstra比较快,但解决不了负权的问题(只要没负权就优先选)
Floyd能解决负权的问题,就是太慢
Bellman-Ford用的化也是优化后用-->SPFA:能解决检查负环,但有时会被针对😓