最短路算法

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.

posted @ 2024-08-18 10:37  zhouyiran2011  阅读(15)  评论(0编辑  收藏  举报