1.floyd算法

Floyd算法是一个优雅而强大的算法,它展示了动态规划思想的简洁美。虽然O(n³)的时间复杂度限制了它在超大规模图上的应用,但在节点数适中的情况下,它依然是最简单易用的所有点对最短路径求解方案。

作为计算机科学学习者,掌握Floyd算法不仅能帮助你解决实际问题,更能加深对动态规划和图论的理解。当你面对一个中等规模的图,需要快速得到所有点对最短路径时,不妨试试这个只有10行代码却蕴含深邃思想的算法。

数据类型的选择:

小规模(n ≤ 200):使用vector<vector>

大规模(n ≤ 500):使用静态二维数组

超大规模:考虑其他算法

INF的选择:

使用INT_MAX/2避免加法溢出

或使用0x3f3f3f3f(约10^9)

性能优化:

循环顺序固定为k, i, j

使用局部变量缓存

提前跳过无效状态

适用场景:

节点数 ≤ 500的稠密图

需要所有点对最短路径

状态转移方程(基本在所有floyd题目适用):dp[k][i][j]=min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]);

例题:USACO08OPEN:Clear And Present Danger S

题解(最优解)
#include<bits/stdc++.h>
using namespace std;
long long ans,n,m,a[10005],f[105][105][105];
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++)cin>>a[i];
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)cin>>f[0][i][j];//可以省下一个danger数组
	for(int k=1; k<=n; k++)
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)f[k][i][j]=min(f[k-1][i][j],f[k-1][i][k]+f[k-1][k][j]);
	for(int i=1; i<m; i++)ans+=f[n][a[i]][a[i+1]];
	cout<<ans;
}

2.dijkstra算法

Dijkstra算法的核心原理可以概括为:

贪心策略:从起点开始,每次选择距离起点最近且未处理的节点,然后尝试通过它更新邻居节点的距离。

三个关键步骤

  1. 选最小:从未处理节点中选出距离最小的节点u
  2. 确定它:u的最短距离已经找到(因为不可能有更短路径)
  3. 松弛:用u去更新所有邻居的距离 dist[v] = min(dist[v], dist[u] + w)

为什么有效:由于边权非负,当前距离最小的节点不可能通过其他节点绕出更短路,因此可以“确定”下来。

限制:不能处理负权边。

例题:P4779 【模板】单源最短路径(标准版)

题解
#include<bits/stdc++.h>
using namespace std;
long long n,m,s,dis[200005];
vector<pair<long long,long long> >node[200005];
priority_queue<pair<long long,long long> >q;
bool vis[200005];
void dijkstra(int s){
  for(int i=1;i<=n;i++)dis[i]=LONG_LONG_MAX;
  dis[s]=0;q.push({0,s});
  while(!q.empty()){
  	int u=q.top().second;
  	q.pop();
  	if(vis[u])continue;
  	vis[u]=1;
  	for(auto j:node[u]){
  		int v=j.first,w=j.second;
  		if(dis[u]+w<dis[v]){
  			dis[v]=dis[u]+w;
  			q.push({-dis[v],v});
  		}
  	}
  }
  
}
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cin>>n>>m>>s;
  for(int i=1;i<=m;i++){
  	int u,v,w;
  	cin>>u>>v>>w;
  	node[u].push_back({v,w});
  }
  dijkstra(s);
  for(int i=1;i<=n;i++)cout<<dis[i]<<' ';
}