图论算法——最短路径算法

求出最短路径的长度

Floyed——Warshall算法 O(n^3)

Floyed算法,可以计算图中任意两点之间的最短路径,适用于出现负边权的情况。

算法描述
1.初始化:点u、v如果有边相连,则dis[u][v]=w[u][v];
如果不相连,则dis[u][v]=0x7fffffff.
2.

for(k=1;k<=n;++k)
	for(i=1;i<=n;++i)
		for(j=1;j<=n;++j)
			if(dis[i][j]>dis[i][k]+dis[k][j])
			dis[i][j]=dis[i][k]+dis[k][j];
		/*如果有另外一条路比当前i到j的长度要短,那么我们就跟新当前的路径长度
		比如这里i到j的路径长度比i到k之后再从k到j的路径长度要长,于是我们更新i到j的路径长度
		*/

3.算法结束,dis[i][j]得出的就是从i到j的最短路径算法。

算法变形

for(k=1;k<=n;++k)
	for(i=1;i<=n;++i)
		for(j=1;j<=n;++j)
			if(dis[i][j]>dis[i][k]+dis[k][j])
			dis[i][j]=dis[i][j]||(dis[i][k]&&dis[k][j]);
	//用这个办法可以判断一张图中的两点是否相连通

Dijkstra算法 O(N^2)

用来计算从一个点到其他所有点的最短路径算法,是一种单源最短路径算法。
Dijkstra不能处理存在负边权的情况。

算法描述
设起点为s,dis[v]表示s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。
初始化:
dis[v]=一个很大的值(v!=s),
dis[s]=0,
pre[s]=0;
for(i=1;i<=n-1;++i)
①在没有被访问过的点中找一个顶点u使得dis[u]是最小的。
②u标记为已确定最短路径
③for与u相连的每个未确定最短路径的顶点v。
if(dis[u]+w[u][v] dis[v]=dis[u]+w[u][v];
pre[v]=u;
}
算法结束:dis[v]为s到v的最短距离,pre[v]为v的前驱节点,用来输出路径。

O(n^2)的dijkatra

void dijkstra(int x) {
	int k;
	for(int i=1;i<=n;++i) dis[i]=a[x][i];
	dis[x]=1;f[x]=1;
	for(int i=1;i<=n-1;++i) {
		int minn=0x3f3f3f3f;
		for(int j=1;j<=n;++j) {
			if( f[j]==0 && dis[j]<minn ){k=j;minn=dis[j];}
		}
		f[k]=1;
		if(k==y) break;
		for(int j=1;j<=n;++j)
			if( f[j]==0 && dis[k]+a[k][j]<dis[j]) 
				dis[j]=dis[k]+a[k][j];
	}
}

优化后的dijkstraO(n+mlogm)

#include
#include
#include
#include
#include
#include
using namespace std;
//dijkstra O(N^2) 
//蓝桥杯的这道题不能用n^2版的dijkstra,有负边权只能用n+mlogm
struct arr {
	int nd,nx,co;
}bot[200005];
struct node{
	int u,d;
};
bool operator < (node a,node b) {//重载运算符,把大根堆转换为小根堆
	if(a.d==b.d) return a.u>b.u;
	else return a.d>b.d;
}
int n,m,head[20005],dis[20005],cnt=0;
priority_queue<node>q;
inline int read() {//读入优化
	int x=0,w=1;char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') w=-1,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
	return x*w;
}
inline void add(int u,int v,int c) //建立邻接链表
{bot[++cnt].nd=v;bot[cnt].co=c;bot[cnt].nx=head[u];head[u]=cnt;}
void dijkstra(int s) {
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	q.push((node){s,0});
	while(!q.empty()) {//当优先队列中不为空时
		node x=q.top();
		q.pop();
		int u=x.u;
		if(x.d!=dis[u]) continue;
		for(int i=head[u];i;i=bot[i].nx) {
			int v=bot[i].nd;
			if(dis[u]+bot[i].co<dis[v]) {//如果有另一条路径到这个点更短,就跟新答案
				dis[v]=dis[u]+bot[i].co;
				q.push((node){v,dis[v]});//放入小根堆中
			}
		}
	}
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;++i) {
		int a=read(),b=read(),c=read();
		add(a,b,c);
	}
	dijkstra(1);
	for(int i=2;i<=n;++i) printf("%d\n",dis[i]);
	return 0;
} 

题目链接 https://www.luogu.com.cn/problem/P1462

几种不同的dijkstra关键是使用的容器不太一样,有的是大根堆,有的是小根堆,重载的方法

关于c++中stl中的优先队列的小根堆和大根堆的解释以及重载
(网上一位大佬的博客)

SPFA

inline void SPFA(int s){//SLF优化的SPFA
    for(int i=1;i<=n;i++) dist[i]=0x3f3f3f3f;
    dist[s]=0;q.push_back(s);
    while(!q.empty()){
        int u=q.front();
        q.pop_front();
        f[u]=0;
        for(int i=head[u];i;i=bot[i].nx){
            int v=bot[i].nd;
            if(dist[v]>dist[u]+bot[i].co){
                dist[v]=dist[u]+bot[i].co;
                pre[v]=u;
                if(!f[v]){
                    f[v]=1;
                    if(q.empty()||dist[v]>dist[q.front()]) q.push_back(v);
                    else q.push_front(v);SLF优化
                }
                //不加slf优化的就是直接放入队列的后面
            }
        }
    }
}

网上有关最短路的一篇详细讲解的博客 https://blog.csdn.net/action_now_zj/article/details/79158598

你可能感兴趣的:(蓝桥杯,图论)