2.5.2 图的表示
邻接表可以直接用如下代码表示:
1 | vector<int> graph[MAX_V] 来表示邻接表 |
2.5.4 最短路问题
1. 单源最短路问题
Bellman-Ford算法
记从起点s出发到顶点i的最短距离为d[i],则下述等式成立:
设初值d[s]=0, d[i]=INF,再不断使用这条递推关系式更新d的值。只要图中不存在负圈,这样的更新操作就是有限的。
1 | struct edge {int from, to, cost}; |
如果图中不存在负圈,最短路径不会经过同一个顶点两次,while(true)最多执行|V|-1次(|V|个顶点,|V|-1条边),因此复杂度为 $O(|V|\times|E|)$。反之如果存在负圈,那么第|V|次循环种也会更新d的值,因此可以通过判断第V次是否仍更新了判断是否存在负圈。
Dijkstra算法
在没有负边的情况下,上述Bellman-Ford算法复杂度高很大一部分原因是,如果d[i]还不是最短距离即使进行d[j]=d[i]+(从i到j的边的权值)的更新,d[j]也不会是最短距离,而且即使d[i]没有变化,每一次循坏也要检查一遍从i出发的所有边,很浪费时间。因此可以对算法做出如下修改:
- 找到最短距离已经确定的顶点,从它出发更新相邻顶点的最短距离
- 此后不用再关心1中的“最短距离已经确定的顶点”
下面是使用STL的priority_queue的实现。在每次更新时往堆里插入当前最短距离和顶点的值对,当取出的最小值不是最短距离时,就丢弃这个值。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27struct 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));
}
}
}
}
2.5.6 图算法的应用
一共有n头牛,按编号顺序排成一排,有ml个关系好的牛的信息,有md个关系不好的牛的信息,对应输入的第一行的三个元素,接下来ml行,每行三个元素A,B,D,表示A牛和B牛相距不希望超过D,接下来md行,每行三个元素A,B,D表示A牛和B牛的相距至少要有D才行。求1号牛和n号牛的最大距离,如果距离无限大输出-2,如果无解输出-1。
记第i头牛的位置是d[i]。首先,牛是按照编号顺序排列的,所以有$d[i] \le d[i+1]$。