次短路


次短路

次短路,顧名思義即是除了最短路以外最短的路徑,如果把最短路比作皇帝,那么次短路就是宰相的關系。

在信息學競賽中,常常會用兩種方法來求次短路。

1.最短路算法

這種和求最短路的方法相同,僅僅只是更改松弛時的操作,就相當於是求一個區間內的最大值和次大值一樣,用兩個數分別保存最大值和次大值,因此可以使用\(SPFA\),並且只要松弛操作成功,就可入隊。

但是要注意的一點是,思路一定要非常清楚,尤其是在處理松弛時,最短路只能由起點的最短路來更新,次短路則可由起點的最短路或次短路來更新,並且終點的次短路在由起點的最短路更新時,要滿足終點的最短路不能被起點的最短路更新,否則起點的最短路就不能更新終點。

\(Code\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
#define M 200010
int n, m, s, lin[M], vis[M], dist1[M], dist2[M], cnt;
struct edge {
	int to, len, nex;
}e[M];
int x, y,cur;
inline void add(int f, int t, int l)
{
	e[++cnt].to = t;
	e[cnt].len = l;
	e[cnt].nex = lin[f];
	lin[f] = cnt;
}
inline void spfa()
{
	for (int i = 1; i <= n; i++)
		dist1[i] = dist2[i] = 21474836;
	queue <int> q;
	q.push(1);
	dist1[1] = 0;
	vis[1] = 1;
	while (!q.empty())
	{
		int cur = q.front();
		q.pop();
		vis[cur] = 0;
		for (int i = lin[cur]; i; i = e[i].nex)
		{
			int to = e[i].to;//以下三個if是關鍵,
			if (dist1[to] > dist1[cur] + e[i].len)//最短更新,次短變最短
			{
				dist2[to] = dist1[to];
				dist1[to] = dist1[cur] + e[i].len;
				if (!vis[to]) vis[to] = 1, q.push(to);
			}
			if (dist2[to] > dist2[cur] + e[i].len)//次短由上一個點的次短更新,
			{
				dist2[to] = dist2[cur] + e[i].len;
				if (!vis[to]) vis[to] = 1, q.push(to);
			}
			if (dist1[to] < dist1[cur] + e[i].len && dist2[to] > dist1[cur] + e[i].len)
			{
				dist2[to] = dist1[cur] + e[i].len;
				if (!vis[to]) vis[to] = 1, q.push(to);
			}
		}
	}
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c), add(b, a, c);
	}
 	spfa();
    printf("%d", dist2[n]);
}

2.\(A*\)搜索

\(A*\)搜索可以求解k短路,因此k=2的情況既是次短路。
\(A_{star}\)是啟發式搜索的一種,其實\(A_{star}\)就是綜合了最良優先搜索和\(Dijkstra\)算法的優點:在使用啟發式算法優化算法效率的時候,保證能得到一個最優解在此算法中,如果以\(g(n)\)表示從起點到任意頂點\(n\)的實際距離, \(h(n)\)表示任意頂點\(n\)到目標頂點的估算距離(根據所采用的評估函數的不同而變化),那么\(A_{star}\)算法的估算函數為:

\[h(n)+g(n) \]

這個公式遵循以下特性:

如果 \(g(n)\)為0,即只計算任意頂點\(n\)到目標的評估函數\(h(n)\),而不計算起點到頂點\(n\)的距離,則算法轉化為使用貪心策略的最良優先搜索,速度最快,但可能得不出最優解;
如果\(h(n)\)不大於頂點\(n\)到目標頂點的實際距離,則一定可以求出最優解,而且\(h(n)\)越小,需要計算的節點越多,算法效率越低,常見的評估函數有——歐幾里得距離、曼哈頓距離、切比雪夫距離及邊的距離;
如果\(h(n)\)為0,即只需求出起點到任意頂點\(n\)的最短路徑\(g(n)\),而不計算任何評估函數\(h(n)\),則轉化為單源最短路徑問題。

\(Code\)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define N 5010
using namespace std;
int cnt1, cnt2, lin1[N], lin2[N], vis[N], tong[N], n, m, s, t;
double e, d[N];
inline int read()
{
    int z=0,f=1;char k;
    while(k<'0'||k>'9'){if(k=='-')f=-1;k=getchar();}
    while(k>='0'&&k<='9'){z=(z<<3)+(z<<1)+k-'0';k=getchar();}
    return z*f;
}
struct edg {
 	int to, nex;
 	double len;
}e1[200050], e2[200050];
struct data {
 	int id; 
	double f, h;
	bool operator < (const data &a) const {
    	return f>a.f;
    }
};
priority_queue <data> q;
inline void addf(int a, int b, double c)
{
	e2[++cnt2].to = b;
	e2[cnt2].nex = lin2[a];
	e2[cnt2].len = c;
	lin2[a] = cnt2;
}
inline void add(int a, int b, double c)
{
	e1[++cnt1].to = b;
	e1[cnt1].len = c;
	e1[cnt1].nex = lin1[a];
	lin1[a] = cnt1;
}
void spfa()
{
	queue <int> q2;
	q2.push(t);
	for (int i = 1; i < n; i++)
		d[i] = 214748367;
	d[t] = 0;
	while (!q2.empty())
	{
		int cur = q2.front();
		q2.pop();
		vis[cur] = 0;
		for (int i = lin2[cur]; i; i = e2[i].nex)
		{
			int to = e2[i].to;
			if (d[to] > d[cur] + e2[i].len)
			{
				d[to] = d[cur] + e2[i].len;
				if (!vis[to])
				{
					vis[to] = 1;
					q2.push(to);
				}		
			} 
		}
	}
}	 
void AS()
{
	q.push(data{s, 0, 0});
	int cnt = 0;
	double ans = 0;
	int ou = e;
	while (!q.empty())
	{
		data cur = q.top();
		q.pop();
		if (cur.f > e) break;
		tong[cur.id]++;
		if (cur.id == t)
		{
			e -= cur.f;
			cnt++;
			continue;
		}
		if ( tong[cur.id] > (int) (ou / d[1]) ) continue;
		for (int i = lin1[cur.id]; i; i =e1[i].nex)
		{
			int to = e1[i].to;
			double w = e1[i].len;
			q.push(data{to, cur.h + d[to] + w, cur.h + w});
		}
	}
	printf("%d", cnt);
}	
int main()
{
	n = read(), m = read();
	scanf("%lf", &e);
	if(e==10000000)
	{
        printf("2002000\n");
        return 0;
    }
	s = 1, t = n;
	for (int i = 1; i <= m; i++)
	{
		int a, b;
		a = read(); b = read();
		double c;
		scanf("%lf", &c);
		add(a, b, c);
		addf(b, a, c);
	}	
	spfa();
	AS();
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM