一。单源最短路问题1。(Bellman-Ford)
1.当图为DAG时,把图拓扑排序一下,然后用递推关系d[i] = d[j] + e(i, j)。
2.当图有圈且不存在负圈时,无法一下子全部递推出来,我们用一个循环套住递推式子,循环在每个节点都算出来时退出。
struct edge{int from, to, cost;};
edge es[MAX_E];
int d[MAX_V];
int V, E;
从s到所有点的最短距离
void shortest_path(int s)
{
for(int i = 0; i < V; i++)
d[i] = INF;
d[s] = 0;
while(true)
{
bool update = false;
for(int i = 0; i < E; i++){
edge e = es[i];
if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost){
d[e.to] = d[e.from] + e.cost;
update = true;
}
}
if(!update)
break;
}
}
用这个算法能检验负圈:
用Bellman_Ford检查负圈
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;
}
二。单源最短路问题2.(Dijkstra)
在上一个算法中,如果d[i]没有最短,那么由d[i]递推出来的都是无用的。而且,如果d[i]没有变化,每次循环也要检查一遍所有i链接的顶点。Dijkstra算法则是从已经确定是最短的顶点往后推。已确定的距离最短的点就是没用过的点中d[i]最小的i,这些d[i]一定是有已经确定的最短点推出来的。d数组初始化为足够大的数字。
int cost[MAX_V][MAX_V];
int d[MAX_V];
bool used[MAX_V];
int V;
void dijkstra()
{
fill(d, d + V, INF);
fill(used, used + V, false);
d[s] = 0;
while(true)
{
int v = -1;
for(int u = 0; u < V; u++){
if(!used[u] && (v == -1 || d[u] < d[v]) )
v = u;
}
if(v == -1)
break;
used[v] = true;
for(int u = 0; u < V; u++)
d[u] = min(d[u], d[v] + cost[v][u]);
}
}
找最小点的操作可以用堆来优化。
堆优化
struct edge{int to, cost; };
typedef pair<int, int> P;
int V;
vector<edge> G[MAX_V];
int d[MAX_V];
void dijkstra(int s)
{
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d + V, INF);
d[s] = 0;
que.push(P(0, s));
while(!que.empty())
{
P p = que.top();que.pop();
int v = p.second;
if(d[v] < p.first)
continue;
for(int i = 0; i < G[v].size(); i++){
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost){
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
如果存在负边,要用第一种。
三。任意两点间的最短路(Floyd_Warshall)
用DP解决。这和走不同路捡不同数量苹果差不多。
如果顶点上限为k:逗号左边是不含k,右边是含k。
状态转移方程:c[i, j, k]=min{c[i, j, k-1], c [i, k, k-1]+c [k, j, k-1]},k>0。
实现时不用为上限k多给数组开一维
int d[MAX_V][MAX_V];
int V;
void warshall_floyd()
{
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, j], d[i][k] + d[k][j]);
}
一定先递增枚举k,这样k才能从k-1转移过来。