最短路径算法笔记

最短路径算法主要包括 \(floyd\) 算法,\(dijkstra\) 算法, \(spfa\)算法

\(Floyd\) 算法

时间复杂度:\(O(n^3)\)

是用来求任意两个结点之间的最短路的。

复杂度比较高,但是常数小,容易实现(只有三个$ for $)。

适用于任何图,不管有向无向,边权正负,但是最短路必须存在。(不能有个负环)

代码模板
//floyd
for (int k = 1; k <= n; k++)
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			g[i][j] = min(g[i][k] + g[k][j], g[i][j]);

\(spfa\) 算法

时间复杂度:一般 \(O(m)\), 最坏\(O(nm)\)

\(SPFA\) 算法是求解单源最短路径问题的一种算法,由理查德·贝尔曼(Richard Bellman) 和 莱斯特·福特 创立的。有时候这种算法也被称为$ Moore-Bellman-Ford $算法。它的原理是对图进行V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达 O(VE)。但算法可以进行若干种优化,提高了效率。可以判负环。

代码模板
void spfa(int u) {
	memset(dis, 0x3f3f3f3f, sizeof dis);
	queue<int> q;
	/*	如果单纯判断负环则将所有点放入队列
		for (int i = 1; i <= n; i++) {
			q.push(i);
			st[i] = 0;
		}
		必要时用超级源点
	*/
	dis[u] = 0;
	st[u] = 1;
	q.push(u);
	while (q.size()) {
		int u = q.front();
		q.pop();
		st[u] = 0;
		for (auto t : v[u]) {
			int e = t.first;
			int w = t.second;
			if (dis[e] > dis[u] + w) {
				dis[e] = dis[u] + w;
				cnt[e] = cnt[u] + 1;
				//判环
				if (cnt[e] >= n) {
					cout << "Yes" << '\n';
					return;
				}
				if (st[e] == 0) {
					q.push(e);
					st[e] = 1;
				}
			}
		}
	}
	if (dis[n] <= 1e17)
		cout << dis[n] << '\n';
	else
		cout << "impossible\n";
}

\(dijkstra\) 算法

时间复杂度:朴素版 \(O(n^2)\) 堆优化版 \(O(mlogn)\)

$Dijkstra $算法由荷兰计算机科学家 E. W. Dijkstra 于 1956 年发现,1959 年公开发表。是一种求解 非负权图 上单源最短路径的算法。

代码模板
朴素版
void dijkstra(){
    for(int i=1;i<=n;i++) dis[i]=1e18;
    dis[1]=0;
    for(int i=1;i<=n;i++)
    {
        int t=-1;
        for(int j=1;j<=n;j++)
        {
            if(st[j]==0)
            {
                if(t==-1||dis[j]<dis[t]) t=j;
            }
        }
        st[t]=1;
        for(int j=1;j<=n;j++) dis[j]=min(dis[j],dis[t]+g[t][j]);
    }
    if(dis[n]==1e18) cout<<-1;
    else cout<<dis[n];
}

堆优化版
void dijkstra() {
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
	for (int i = 1; i <= n; i++)
		dis[i] = 1e18;
	memset(st, 0, sizeof(st));
	dis[1] = 0;
	q.push({0, 1});
	while (q.size()) {
		int u = q.top().second;
		q.pop();
		if (st[u] != 0)
			continue;
		st[u] = 1;
		for (auto e : g[u]) {
			int v = e.first; //点
			int w = e.second; //权值
			if (dis[v] > dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!st[v])
					q.push({dis[v], v});
			}
		}
	}
}

图论技巧

1.超级源点
2.超级汇点

讲解题目

1.P3371 【模板】单源最短路径(弱化版)
2.P4779 【模板】单源最短路径(标准版)
3.P3385 【模板】负环
4.B3647 【模板】Floyd

练习题目

1.P1744 采购特价商品
2.F - 这是个求最短路的题
3.P1821 [USACO07FEB] Cow Party S
4.P2299 Mzc和体委的争夺战

拓展博客:https://www.cnblogs.com/ZhangDT/articles/18286317

posted @ 2025-01-16 10:59  ZhangDT  阅读(308)  评论(0)    收藏  举报