zoukankan      html  css  js  c++  java
  • 次短路

    次短路

    次短路,顾名思义即是除了最短路以外最短的路径,如果把最短路比作皇帝,那么次短路就是宰相的关系。

    在信息学竞赛中,常常会用两种方法来求次短路。

    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
    ");
            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;
    }
    
    
  • 相关阅读:
    bzoj2959
    学习笔记::lct
    bzoj3203
    bzoj1319
    bzoj3625
    bzoj3992
    bzoj1565
    bzoj3513
    平常练习动归(1.胖男孩)———最长公共子序列
    2016 noip 复赛 day2
  • 原文地址:https://www.cnblogs.com/liuwenyao/p/10162462.html
Copyright © 2011-2022 走看看