最短路问题
符号说明
- V表示点集,E表示边集
- 斜体表示有坑待填
Dijkstra
-
vis数组的理解
先把V分成两组:
- S:已求出最短路径的顶点的集合
- V-S=T:尚未确定最短路径的顶点集合
将T中顶点按最短路径递增的次序加入到S中
即用T集合中距离源点最近的更新所有边,并把这个最近点放入S中
因为刚开始S为空,所以最外层为循环为V次
-
堆优化
即把求最近点的过程放入小根堆中,vis数组一样适用
-
有一点 BFS
-
斐波那契堆
//normal
dis[s]=0;
for(int i=0;i<n;i++)
{
int minj=0,mindis=inf;
for(int j=1;j<=n;j++)
if(dis[j]<mindis && !vis[j])
{
mindis=dis[j];
minj=j;
}
for(int j=head[minj];j!=-1;j=edge[j].nex)
if!vis[edge[i].v] && (dis[edge[j].v]>dis[minj]+edge[j].w)
dis[edge[j].v]=dis[minj]+edge[j].w;
vis[minj]=true;
}
//heap
for(dis[s]=0,q.push(nodeq{s,dis[s]});!q.empty();q.pop())
{
nodeq tmp=q.top();
if(vis[tmp.i])continue;
for(int i=head[tmp.i];i!=-1;i=edge[i].nex)
{
if(!vis[edge[i].v] && dis[tmp.i]+edge[i].w<dis[edge[i].v])
{
dis[edge[i].v]=dis[tmp.i]+edge[i].w;
q.push(nodeq{edge[i].v,dis[edge[i].v]});
}
}
vis[tmp.i]=true;
}
Bellman-Ford
- 就是不断地进行松弛操作
- 实际上是一个建树过程
- 一点优化:一轮中未发生松弛则退出循环
- 判负环:枚举所有边,若能松弛,则存在负环
- 因为树最多有V-1条边,所以最外层为循环为V-1次
for(int i=0;i<n-1;i++){
bool flag=false;
for(int j=0;j<2*m;j++){
if(dis[edge[j].u]<inf){
dis[edge[j].v]=min(dis[edge[j].v],dis[edge[j].u]+edge[j].w);
flag=true;
}
}
if(!flag)break;
}
SPFA
- 上面的BF算法实际是一个建树过程,那么轮数小于节点深度的松弛实际上是没有用处的,SPFA就是用队列保存需要松弛的边
- 入队:
vis[i]=true
出队:vis[i]=false
- 关于队列中相同的点只有一个:入队-->需要更新-->队列中已有的那个会负责更新
- dfs优化
//normal
dis[s]=0;
for(q.push(s);!q.empty();q.pop())
{
int u=q.front();
for(int i=head[u];i!=-1;i=edge[i].nex)
{
if(dis[u]+edge[i].w<dis[edge[i].v])
{
dis[edge[i].v]=dis[u]+edge[i].w;
if(!vis[edge[i].v])
{
q.push(edge[i].v);
vis[edge[i].v]=true;
}
}
}
vis[u]=false;
}
//slf
dis[s]=0;
for(deq.push_back(s);!deq.empty();deq.pop_front())
{
int u=deq.front();
for(int i=head[u];i!=-1;i=edge[i].nex)
{
if(dis[u]+edge[i].w<dis[edge[i].v])
{
dis[edge[i].v]=dis[u]+edge[i].w;
if(!vis[edge[i].v])
{
if(dis[edge[i].v]<dis[q.front()])
deq.push_front(edge[i].v);
else
deq.push_back(edge[i].v);
vis[edge[i].v]=true;
}
}
}
vis[u]=false;
}
Floyd
-
k在外层(
dis(A,C)!=9
)
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(!(dist[i][k]==Inf||dist[k][j]==Inf) && dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];
其他
-
路径保存:松弛操作时记录该节点的父亲节点
-
链式前向星:
//有向 struct node{int v;llg w;int nex;}edge[maxm]; int head[maxn],cnt; void add(int u,int v,llg w){ edge[cnt].v=v; edge[cnt].w=w; edge[cnt].nex=head[u]; head[u]=cnt++; }