最近集训有三个老师讲到过最短路径问题,所以现在把求最短路径的几种方法总结一下。
一、Floyd-Warshall算法
求两点间的最短距离,可以用DP来求解任意两点间的最短距离。从点1到点2的距离,无非两种情况,第一种是直接从点1到点2,第二种是点1先到其他点,然后再到点二。由此就可以得出状态转移方程:dp[i][j] = min ( dp[i][j], dp[i][k] + dp[k][j] ),这个方程应该很好理解,所以下面直接给出代码。
int d[][];//d[u][v]表示权值,d[u][u] = 0
int V[];//顶点数
void Floyd_Warshall()
{
for(int k = 0; k < V; k++)
for(int i = 0; i < V; i++)
for(int j = 0; j < V; j++)
d[i][j] = min(d[i][k], d[k][j]);
}
可以思考一下,为什么把节点K放在最外层?
参考博客:http://chenchuangfeng.iteye.com/blog/1816976
二、Dijkstra算法
我认为,这个算法的主要思想就是贪心。令集合S = {V0},T = {V其他},然后一次把T中的距离S集合最近的点依次加入到S中,用一个数组记录下最近距离。
void Dijkstra()
{
for(int i = 1; i <= N; i++)
dis[i] = map[1][i];
dis[1] = 0;
memset(vis, false, sizeof(vis));
vis[1] = true;
for(int i = 1; i <= N; i++)
{
int min = inf, k;
for(int j = 1; j <= N; j++)
{
if(!vis[j] && dis[j] < min)
{
min = dis[j];
k = j;
}
}
vis[k] = true;
for(int j = 1; j <= N; j++)
if(!vis[j] && dis[k] + map[k][j] < dis[j])
dis[j] = dis[k] + map[k][j];
}
}
三、Bellman-Ford算法
不知道怎么把这个算法讲清楚,请直接参考:http://blog.sina.com.cn/s/blog_6803426101014l1v.html
struct edge
{
int from;
int to;
int cost;
}es[20010];
void Bellman_Ford()
{
for(int i = 1; i <= N; i++)
d[i] = INF;
d[1] = 0;
for(int i = 0; i < N - 1; i++)
for(int j = 0; j < M * 2; j++)
d[es[j].to] = min(d[es[j].from] + es[j].cost, d[es[j].to]);
}
该算法还可以检验图中有没有负圈
//若返回true,则存在负圈
bool find_negative_loop()
{
memset(d, 0, sizeof(d));
for(int i = 0; i < V; i++)
{
for(int j = 0; j < E; j++)
{
edge e = es[j];
if(d[e.to] > d[e.from] + e.cost)
{
d[e.to] = d[e.from] + e.cost;
if(i == V - 1)return true;
}
}
}
return false;
}
四、SPFA算法
该算法就是用队列来维护,优化Bellman_Ford算法
直接参考上一算法的链接。