第k短路(A*)

第k短路(A*) (参考的一个acm模板,不知道挂啥链接)

A*算法思想
  1. 广度优先搜索:以广度做为优先级进行搜索。从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点,时间空间开销大。
  2. 深度优先搜索:每次沿着特定规则走,纵向寻找答案,找到答案的时机视具体情况而不同。

对比:假如深度优先在搜索的时候恰好第一次搜索就是正确路径,这个效率就很高,而广度优先是不做决策,同步进行。那么我们有没有办法使得算法在搜索的时候有目的的去搜索呢?答案是肯定的。

启发式搜索(A*)

给出一个估价函数:g(n) = f(n) + h(n),构造一个最小堆。

  • g(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • f(n) 是节点n距离起点的代价。
  • h(n)是节点n距离终点的预计代价。

启发函数

  • 在极端情况下,当启发函数h(n)始终为0,则将由f(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
  • 在另外一个极端情况下,如果h(n)相较于f(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

每次找到终点计数器加一,在第k次到达终点时(出堆的结点id为终点)即找到了答案。

邻接矩阵
/*==================================================*\
 | 第 K 短路(A*)
 | A* 估价函数 fi为到当前点走过的路经长度, hi为该点到终点的长度
 | gi = hi + fi;
\*==================================================*/
//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
using namespace std;

int n, m, k; 
int g[1010][1010], gr[1010][1010]; 
int dist[1010], v[1010];   //dist[i] : i到n-1的最短路长, v[i]:标记一访问
const int INF = 1000000000; 

struct node{ 
 	int id, fi, gi; 
	node (){};
	node (int _id, int _fi, int _gi) : id(_id), fi(_fi), gi(_gi) {};
 	friend bool operator <(const node& a, const node& b){ 
 		if (a.gi == b.gi) return a.fi > b.fi; 			//优先走到最近的
 			return a.gi > b.gi; 		//优先总路径最小
 	} 
}; 

priority_queue<node> q;

int init(){ 			//反向dijikstra求出dist[]
 	for (int i = 0; i <= n; i++){ 
 		dist[i] = INF; 
 		v[i] = 1; 
 	} 
 	dist[n-1] = 0; 			//从终点n-1出发
 	for (int i = 0; i < n; i++){ 
 		int k = n; 
 		for (int j = 0; j < n; j++) 
 			if (v[j] && dist[j] < dist[k]) k = j; 
 			if (k == n) break; 
 			v[k] = 0; 
 		for (int j = 0; j < n; j++) 
 		if (v[j] && dist[k] + gr[k][j] < dist[j]) 
 			dist[j] = dist[k] + gr[k][j]; 
 	} 
 	return 1; 
} 

int solve(){ 
 	if (dist[0] == INF) return -1; 
	int cnt = 0;		//第几次找到最短路路径
 	q.push(node(0, 0, dist[0]));
	while (!q.empty()){
		node cur = q.top();
		q.pop();
		if (cur.id == n-1)	cnt++;
		if (cnt == k)	return cur.fi;
		for (int i = 0; i < n; ++i)
			if (g[cur.id][i] != INF)
				q.push(node(i, cur.fi + g[cur.id][i], dist[i]));
	}
 	return -1; 
} 

int main(){ 
 	while (scanf("%d %d %d", &n, &m, &k) != EOF){ 
 		for (int i = 0; i < n; i++) 
 			for (int j = 0; j < n; j++) 
 				gr[i][j] = g[i][j] = INF; 
 		for (int i = 0; i < m; i++){ 
 			int x, y, z; 
 			scanf("%d %d %d",&x, &y, &z); 
 			x--, y--; 
 			g[x][y] = z; 
			gr[y][x] = z;	//反向建图
 		} 
 		init(); 
 		printf("%d\n", solve()); 
 	} 
 	return 0; 
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值