首页 > 其他分享 >次短路和第 k 短路

次短路和第 k 短路

时间:2024-08-09 15:38:27浏览次数:6  
标签:结点 dist int 短路 cnt dis

次短路和第 k 短路

次短路

在最短路的基础上,次短路可以由次短路\(~ +~\)边更新,也可以由最短路$~
+~边更新,这里注意一点,因为次短路更新时也会对其它次短路产生影响,所以更新次短路时也需要入队,我们先尝试更新最短路,成功的话就把原来的最短路给次短路,不成功的话就单独尝试更新次短路。

也就是说,我们可以在算法\(~dijkstra~\)的基础上,不断维护最短路和次短路。证明则十分简单,在下面的\(~k~\)短路算法证明时,可以带入次短路,具体实现见代码。

priority_queue<pair<int, int>, vector<vector<int, int> >, greater<pair<int ,int> > > q;
memset(dis, 0x3f, sizeof dis); 
dis[1][1] = 0;//dis[i][1]表示 1 到 i 的最短路,dis[i][2]表示 1 到 i 的次短路
q.push({0, 1});
while(!q.empty())
{
    int x = q.top().second;
    int dist = q.top().first;
    q.pop();
    if(dist > dis[x][2]) continue;
    for(auto it : v[x])
    {
        int cnt = it.first;
        if(dis[cnt][1] > dist + it.second)//如果最短路需要松弛,那么当前的最短路将会被松弛,次短路就会松弛为原先的最短路
        {
            dis[cnt][2] = dis[cnt][1];
            dis[cnt][1] = dist + it.second;
            q.push({dis[cnt][1], cnt});
        }
        if(dis[cnt][2] > dist + it.second && dist + it.second > dis[cnt][1])//如果当前的路径比次短路优,而比最短路劣,那么只需要更新次短路
        {
            dis[cnt][2] = dist + it.second;
            q.push({dis[cnt][2], cnt});
        }
    }
}
return dis[n][2];//返回

那么,关于次短路,应该就结束了。

k 短路

此时,如果按照次短路的做法显然是不可行的,这时候我们需要引入一个叫做启发式搜索的东西。

A* 算法

定义

A* 搜索算法(英文:A* search algorithm,A* 读作 A-star),简称 A* 算法,是一种在图形平面上,对于有多个节点的路径求出最低通过成本的算法。它属于图遍历(英文:Graph traversal)和最佳优先搜索算法(英文:Best-first search),亦是 BFS 的改进。

过程

定义起点 \(s\),终点 \(t\),从起点(初始状态)开始的距离函数 $g(x) $,到终点(最终状态)的距离函数 \(h(x\))$, \(h^{\ast}(x)1\),以及每个点的估价函数 \(f(x)=g(x)+h(x)\)。

A* 算法每次从优先队列中取出一个 \(f\) 最小的元素,然后更新相邻的状态。

如果 \(h\leq h*\),则 A* 算法能找到最优解。

上述条件下,如果 \(h\) 满足三角形不等式,则 A* 算法不会将重复结点加入队列。

当 \(h=0\) 时,A* 算法变为 \(Dijkstra\);当 \(h=0\) 并且边权为 \(1\) 时变为 BFS。

如何用 A* 解决 k 短路问题

A* 算法定义了一个对当前状态 \(x\) 的估价函数 \(f(x)=g(x)+h(x)\),其中 \(g(x)\) 为从初始状态到达当前状态的实际代价,\(h(x)\) 为从当前状态到达目标状态的最佳路径的估计代价。每次取出 \(f(x)\) 最优的状态 \(x\),扩展其所有子状态,可以用 优先队列 来维护这个值。

在求解 \(k\) 短路问题时,令 \(h(x)\) 为从当前结点到达终点 \(t\) 的最短路径长度。可以通过在反向图上对结点 \(t\) 跑单源最短路预处理出对每个结点的这个值。

由于设计的距离函数和估价函数,对于每个状态需要记录两个值,为当前到达的结点 \(x\) 和已经走过的距离 \(g(x)\),将这种状态记为 \((x,g(x))\)。

开始我们将初始状态 \((s,0)\) 加入优先队列。每次我们取出估价函数 \(f(x)=g(x)+h(x)\) 最小的一个状态,枚举该状态到达的结点 \(x\) 的所有出边,将对应的子状态加入优先队列。当我们访问到一个结点第 \(k\) 次时,对应的状态的 \(g(x)\) 就是从 \(x\) 到该结点的第 \(k\) 短路。

优化:由于只需要求出从初始结点到目标结点的第 \(k\) 短路,所以已经取出的状态到达一个结点的次数大于 \(k\) 次时,可以不扩展其子状态。因为之前 \(k\) 次已经形成了 \(k\) 条合法路径,当前状态不会影响到最后的答案。

当图的形态是一个 \(n\) 元环的时候,该算法最坏是 \(O(nk\log n)\) 的。但是这种算法可以在相同的复杂度内求出从起始点 \(s\) 到每个结点的前 \(k\) 短路。

具体实现

因为步骤较为繁琐,所以直接给出完整代码

#include <bits/stdc++.h>
#define IOS ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, pair<int, int> > PIII;
typedef pair<int, int> PII;
int s, t, k, n, m, u, v, z;
struct node
{
	int v;
	int len;
};
vector<node> mp[1010], mp_dij[1010];
int dist[1010];
bool vis[1010];
int cnt[1010];
void dij()
{
	priority_queue<PII, vector<PII>, greater<PII> > q;
	q.push({0, t});
	memset(dist, 0x3f, sizeof dist);
	dist[t] = 0;
	while(q.size())
	{
		int u = q.top().y;
		q.pop();
		if(vis[u]) continue;
		vis[u] = true;
		for(auto it : mp_dij[u])
		{
			int v = it.v;
			if(dist[v] > dist[u] + it.len) dist[v] = dist[u] + it.len, q.push({dist[v], v});
		}
	}
	return;
}
int A_star()
{
	priority_queue<PIII, vector<PIII>, greater<PIII> > q;
	q.push({dist[s], {0, s}});
	while(q.size())
	{
		PIII cur = q.top();
		q.pop();
		int u = cur.y.y;
		int dis = cur.y.x;
		cnt[u] ++;
		if(cnt[t] == k) return dis;
		if(cnt[u] > k) continue;
		for(auto it : mp[u])
		{
			int v = it.v;
			q.push({dis + it.len + dist[v], {dis + it.len, v}});
		}
	}
	return -1;
}
int main()
{
	IOS;
	cin >> n >> m;
	for(int i = 0; i < m; i ++) cin >> u >> v >> z, mp_dij[v].push_back({u, z}), mp[u].push_back({v, z});
	cin >> s >> t >> k;
	if(s == t) k ++;
	dij();
	cout << A_star();
	return 0;
}

标签:结点,dist,int,短路,cnt,dis
From: https://www.cnblogs.com/Liu-Tao-Chang/p/18350853

相关文章

  • 【变压器的短路试验】变压器的短路试验是通过将二次侧短路,并向一次侧施加额定电流来进
       ......
  • Studying-代码随想录训练营day62| Floyd 算法精讲、A*算法精讲(A star算法)、最短路算法
    第62天,完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*,最后的两个算法学习,编程语言C++目录Floyd算法精讲A*算法精讲(Astar算法) A*算法 复杂度分析 A*算法的缺点最短路算法总结篇 图论总结深搜和广搜并查集最小生成树 拓扑排序 最短路算法 总结 Floyd算法精讲......
  • 代码随想录算法训练营第62天 | 最短路径:dijkstra(堆优化版)+ Bellman_ford算法
    47.参加科学大会https://kamacoder.com/problempage.php?pid=1047dijkstra(堆优化版)精讲https://www.programmercarl.com/kamacoder/0047.参会dijkstra堆.html#思路94.城市间货物运输Ihttps://kamacoder.com/problempage.php?pid=1152Bellman_ford算法精讲https://www.pr......
  • 关于最短路
    定义单源最短路:从一个点出发,到其他所有点的最短距离多源最短路:从图中任意一点出发,到其他所有点的最短距离记号\(n~\)为图上点的数目,\(m~\)为图上边的数目;\(s~\)为最短路的源点;\(D(u)~\)为\(~s~\)点到\(~u~\)点的实际最短路长度;\(~dis(u)~\)为s点到u点的估计最短......
  • 关于最短路
    定义单源最短路:从一个点出发,到其他所有点的最短距离多源最短路:从图中任意一点出发,到其他所有点的最短距离记号\(n~\)为图上点的数目,\(m~\)为图上边的数目;\(s~\)为最短路的源点;\(D(u)~\)为\(~s~\)点到\(~u~\)点的实际最短路长度;\(~dis(u)~\)为s点到u点的估计最短......
  • 最短路
    Floyd三方,顺序是\(k,i,j\),理由是我们定义的数组f[k][x][y],表示只允许经过结点\(1\)到\(k\)(也就是说,在子图\(V'={1,2,\ldots,k}\)中的路径,注意,\(x\)与\(y\)不一定在这个子图中),结点\(x\)到结点\(y\)的最短路长度,可以压掉一维。for(intk=1;k<=n;k++) for......
  • 【GeoScene】一、创建、发布路网服务,并在代码中测试最短路径分析
    前言网上关于GeoScene及GeoSceneAPIforJavaScript的资料太少了,官方的技术支持又太慢了,最近把在项目中踩过的坑分享出来;**版本信息**GeoScenePro4.0GeoSceneEnterprise3.1GeoSceneAPIforJavaScript4.27.4一、创建网络分析图层1、在地理数据库中新建......
  • 解密编程的八大法宝(四)(附二分查找、分治法和图论算法(深度和广度优先搜索、最短路径、最
    算法题中常见的几大解题方法有以下几种::暴力枚举法(BruteForce):这是最基本的解题方法,直接尝试所有可能的组合或排列来找到答案。这种方法适用于问题规模较小的情况,但在大多数情况下效率不高。贪心算法(GreedyAlgorithm):贪心算法在每一步都选择当前看起来最优的解,希望最终能......
  • 最短路计数
    //最短路计数.cpp:此文件包含"main"函数。程序执行将在此处开始并结束。///**https://loj.ac/p/10077【题目描述】给出一个N个顶点M条边的无向无权图,顶点编号为1∼N。问从顶点1开始,到其他每个点的最短路有几条。输入格式第一行包含2个正整数N,M,为图的顶......
  • 观光 最短路次短路
    //观光.cpp:此文件包含"main"函数。程序执行将在此处开始并结束。///*https://www.acwing.com/problem/content/description/385/“您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。比荷卢经济联盟有很多公交线路。每天公共汽车都会从一座城市开往另一......