最短路算法
1.Dijkstra算法
算法思想:\(dijkstra\) 算法采用的是贪心的思想。
(1)定义一个\(dis\)数组,\(dis[i]\) 表示i点到源点的最短路径,设源点的 \(dis\) 值为0,其他 \(dis\) 值为 \(∞\) 。
(2)选出其中的最小 \(dis\) 值,进行标记并更新它相邻的 \(dis\) 值。
(3)不断循环操作(2)。
优点:dijkstra 算法可以广泛使用于大多数题目。
缺点: dijkstra 算法不能在图中有负权环时使用。
核心代码:
void dijkstra()
{
for(int i=1;i<=n;i++)d[i]=INF;
dis[1]=0;
for(int i=1;i<=n;i++)
{
int mark=-1;
mindis=INF;
for(int k=1;k<=n;k++)
{
for(int j=head[i];j;j=edge[j].nxt)
{
if(!vis[edge[j].to]&&edge[j].dis<mindis)
{
mindis=edge[j].dis;
mark=edge[j].to;
}
}
}
vis[mark]=1;
for(int i=head[mark];i;i=edge[i].nxt)
{
if(!vis[edge[i].to])
{
d[edge[i].to]=min(d[edge[i].to],d[mark]+edge[i].dis);
}
}
}
}
堆优化:
struct node{
int dis,pos;
bool operator<(const node &x) const
{
return x.dis<dis;
}
};
inline void dij(int s)
{
for(int i=1;i<=n;i++) dis[i]=0x3f3f3f3f3f3f3f3f, vis[i]=0;
priority_queue<node> q;
dis[s]=0, q.push((node){0, s});
while(q.size())
{
int u=q.top().pos;
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)
{
dis[v]=edge[i].w+dis[u];
q.push((node){dis[v], v});
}
}
}
}
2. Bellmax-ford算法
(1) 定义一个 \(dis\) 数组,\(dis[i]\) 表示 \(i\) 点到源点的最短路径,设源点的 \(dis\) 值为0,其他 \(dis\) 值为 \(∞\) 。
(2) 从图的任意边开始,对每一条边进行松弛操作(对于边 \(u>v\) ,如果 \(dis[v]>dis[u]+w[u][v]\),\(dis[v]=dis[u]+w[u][v]\) , \(w\) 为图的权值)①,重复点数-1次。
(3)最后再进行一边松弛操作,如果存在①,则存在负权环。
优点:Bellman-ford算法边的权值可以为负数,并可检测负权回路。
缺点:时间复杂度过高。
核心代码:
bool bellman(int v0)
{
for(int i=1;i<=nv-1;i++)
{
for(int j=1;j<=ne;j++)
{
if(dis[edge[j].a]+edge[j].w<dis[edge[j].b])
{
dis[edge[j].b]=dis[edge[j].a]+edge[j].w;
}
}
for(int j=1;j<=ne;j++)
{
if(d[edge[j].a]+edge[j].w<dis[edge[j].b])
{
return 0;
}
}
return 1;
}
}
3. SPFA算法:
算法思想:SPFA算法实际是\(Bellman-ford\)算法的队列优化。
(1) 定义一个\(dis\)数组,\(dis[i]\) 表示i点到源点的最短路径,设源点的 \(dis\) 值为0,其他 \(dis\) 值为 \(∞\) 。定义队列 \(q\) ,源点进队。
(2) 从队列中取出队首元素 \(u\),标记节点 \(u\) 出队,对u相连的所有节点 \(v\) 进行松弛操作。如果松弛成功,检查节点 \(v\) 进队次数,如果超过 \(|V|\),则说明出现负权环,算法结束;否则,修改 \(dis[v]\) ,检查节点 \(v\) 是否在队列中,如果不在,节点 \(v\) 进队。
(3) 重复操作(2)。
优点:可以解决负权环问题。
核心代码:
void spfa(int u)
{
queue<int> q;
q.push(u), dis[u]=0, vis[u]=1;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].first;
if(dis[v]>dis[u]+edge[i].w||dis[v]<0)
{
dis[v]=dis[u]+edge[i].w;
}
if(vis[v]==1)
{
vis[v]=1;
q.push(v);
}
}
}
}
4. Floyd算法
算法思想:Floyd算法是基于动态规划思想的。
(1) 定义 \(dp[k][i][j]\) 表示从点i到点j只允许经过前k个点得到的最短路径。
(2) 如果经过第\(k\)个点,那么 \(dp[k][i][j]=dp[k-1][i][k]+dp[k-1][k][j]\)。
(3) 如果经过第k个点,那么 \(dp[k][i][j]=dp[k-1][i][j]\)。
(4) \(dp[k][i][j]=min(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])\)。
(5) 边界:\(dp[0][i][j]=g[i][j],g[i][j]\)指\(i\)到\(j\)的边权,当无法到达时可以设为 \(∞\)。
优点:稠密图效果佳,边权可为负。
核心代码:
memset(dis, 0x7f, sizeof(dis));
for(int i=1;i<=m;i++)
{
int u, v;
cin>>u>>v;
dis[u][v]=dis[v][u]=w[u][v];
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if((i!=j)&&(i!=k)&&(j!=k))
{
if(dis[i][j]>dis[i][k]+dis[k][j])
{
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
}
our story begins.