1. 程式人生 > >圖論演算法小結:次短路的求解

圖論演算法小結:次短路的求解

利用Dijkstra演算法求解次短路

我們曾經學過利用Dijkstra演算法求解最短路,但是如果要求解某一個結點的次短路該怎麼做呢?實際上,我們仍然可以用Dijkstra演算法來求解它。

首先來回顧一下Dijkstra演算法的原理:首先把所有結點的最短距離設定為無窮大,然後令d[0]=0。接下來,每次都找到最短路已經確定的經典,更新從它出發的相鄰結點的最短距離。以後我們不再考慮最短距離已經確定了的結點。

以上就是Dijkstra演算法的主要過程,需要注意的一點是我們不再考慮的是“最短距離已經確定了的結點”,不要錯誤地理解為是更新過最短距離值的結點。因為有些結點的最短路可能需要多次更新才能最終確定。那麼問題來了,如何知道哪些結點的最短距離是確定的呢?這裡我們利用了一點貪心的思想,每次都取出當前距離最短的那個結點,認為它的最短路就是已經確定好的。可以證明這樣的做法是正確的。這也算為什麼優化版本的DIjkstra演算法用到了priority_queue的原因。

那麼回到主題,如何求解次短路呢?如果我們要求解起點s到終點t的次短路,那麼有兩種可能的情況:(1)起點s到某個頂點u的最短路+d(u,t)。(2)起點到某個頂點u的次短路+d(u,t)。因此,對於每個結點,我們記錄的不僅僅是最短距離,還有次短距離,接下來用類似於Dijkstra演算法不斷更新這兩個距離即可求出次短路了。

#define N 100000+10
#define INF 100000000
typedef pair<int, int>P;
int n,r;
struct Edge{ int to, cost; };
vector<Edge>G[N];
int dist[N], dist2[N];
void addedge(int u, int v,int w)
{
	G[u].push_back(Edge{ v, w });
	G[v].push_back(Edge{ u, w });
}
void solve()
{
	priority_queue<P, vector<P>, greater<P> >q;
	fill(dist, dist + n, INF);
	fill(dist2, dist2 + n, INF);
	dist[0] = 0;
	q.push(P(0, 0));
	while (!q.empty())
	{
		P u = q.top(); q.pop();
		int v = u.second, d = u.first;
		if (dist2[v] < d)continue;//取出的不是次短距離,拋棄
		for (int i = 0; i < G[v].size(); i++)
		{
			Edge&e = G[v][i];
			int d2 = d + e.cost;
			if (dist[e.to]>d2)//更新最短距離
			{
				swap(dist[e.to], d2);
				q.push(P(dist[e.to], e.to));
			}
			if (dist2[e.to]>d2&&dist[e.to] < d2)//更新次短距離
			{
				dist2[e.to] = d2;
				q.push(P(dist2[e.to], e.to));
			}
		}
	}
	printf("%d\n", dist2[n - 1]);
}