A*算法 k短路径问题——poj2449

目录

前言

dijkstra算法

A*算法

k短路径问题

问题描述

输入

输出

问题分析

如何求第k短路径

如何实现A*算法

代码

有关问题之外


前言

之前我们写过的搜索算法都是一种"盲目"的搜索算法 ,就是说我并不知道我所走的这条路是否可以到达终点,但只要我搜索范围足够大,那么就一定可以到达终点 ,这样的盲目式搜索虽然思路简单但是也造成了很多不必要的搜索,所以我们之后引入了剪枝技术,但这任然改变不了盲目搜索算法的本质,于是A*算法就产生了,它相当于给搜索算法增加了一个感知模块,从而使得搜索算法变得智能。

在开始算法讲解前,先说说本题需要用到的其他搜索技术吧

dijkstra算法

这个算法相当于在传统的bfs 算法的基础上加上**优先队列,**是一个可以搜索起点到每个节点的最短路的最短路径的算法,由于使用了优先队列,使得它可以处理边权不同的图问题

A*算法

这才是这个blog的重点,前面说到传统bfs算法是盲目的,但如果我们可以事先知道,走哪条路径是最优的,那么每次都取最优的方向开始搜索,这不就给算法赋予了方向感了吗。

大家可能已经注意到了,每次去最优的方向,这不就是贪心算法的思路吗,但肯定也有人听过,贪心算法得出的答案不一定是最优的,如果是在没有任何障碍的方格图中,贪心算法是最优的,但如果是其他情况,那就不一定了,我们已知dijkstra算法得出的答案一定是最优的,那么是否可以将两种算法结合一下,那么既可以保证算法的正确性又提升了算法的执行效率,答案是肯定的,这也就得出了A*算法。

A*算法=dijkstra算法+贪心算法。

贪心算法每次优先入队的是距离终点最近的节点,dijkstra算法每次入队的是距离起点最近的节点,那么A*算法就可以每次优先入队距离起点距离+距离终点距离最小的节点,假设起点为s终点为t,起点到达i节点后优先入队s->i+i->t最小的节点。

现在我们来说明A*算法的正确性,如果到达终点时入队的时s->i+i->t最小的节点,由于i->t为0那么入队的就是s->t最小的节点,这个就是答案

k短路径问题

问题描述

给出一个图,由n个点和m条边组成,给定起点s和终点t,求s到t的第k短路径,图中每个点包括起点和终点都可以多次经过。

输入

第一行输入两个整数n,m其中1<=n<=1000, 1<=m<=100000;点从1~n开始编号,之后的m行中每行输入三个整数a,b,w,代表a到b之间有一条单向变,长度为w,最后一行输入三个整数s,t

,k。

输出

输出s到t的第k短路径的长度。

问题分析

如何求第k短路径

我们先解决一个基础问题,如何求第k短路径,之前都是求最短路径,如果bfs算法中第一次搜索到终点时那么就是最短路径,那么同理,如果bfs算法中第k次搜索到终点 时那么这个就是它的第k短路径,可以用一个计数变量实现这一算法

如何实现A*算法

实现A*算法的关键就是设计f(i)函数,其中g(i)函数是到起点的最短路径,h(i)是到终点的理论最短路径,那么h(i)就可以事先用dijkstra算法求出终点到图中各个点的最短路径,从而实现A*算法

这个算法涉及很多个变量,编码时一定要小心

代码

cpp 复制代码
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 1005, M = 100005;
struct edge {  //存储单向边,to代表该单向边的目的地,w代表该单向边的长度
	int to, w;
	edge(int a, int b) {
		to = a;
		w = b;
	}
};

vector<edge>G[M], G2[M];  //G存储正图,G2存储反图
struct node {   //id表示该节点的编号,dis表示该节点到终点的距离(从终点反向搜索)
	int id, dis;
	node(int a, int b) {
		id = a, dis = b;
	}
	//优先队列默认是取出最大的元素,所以需要重新定义
	bool operator <(const node& u)const {
		return dis > u.dis;
	}
};    //这个node节点用于从终点搜索的优先队列中
int dist[N]; //存储节点到终点的距离(存储h函数的值)

void dijkstra(int s) {
	for (int i = 0; i < N; i++) {
		dist[i] = INF; 
	}
	dist[s] = 0;
	priority_queue<node>q;
	q.push(node(s, dist[s]));
	while (!q.empty()) {
		node u = q.top();
		q.pop();
		for (int i = 0; i < G2[u.id].size(); i++) {
			edge y = G2[u.id][i];
			if (dist[y.to] > u.dis + y.w) {
				dist[y.to] = u.dis + y.w;
				q.push(node(y.to, dist[y.to]));
			}
		}
	}
}

struct point {
	int v, g, h;
	point(int a, int b, int c) {
		v = a, g = b, h = c;
	}
	//A_star算法,根据f=g+h判断那个节点先入队
	bool operator <(const point& d)const {
		return g + h > d.g + d.h;
	}
};
int times[N]; //存储节点访问次数
int A_star(int s,int t,int k) {
	memset(times, 0, sizeof(times));
	priority_queue<point>q;
	q.push(point(s, 0, 0));
	while (!q.empty()) {
		point p = q.top();
		q.pop();
		//每次出队算一条路径
		times[p.v]++;
		if (times[p.v] == k && p.v == t) {
			return p.g + p.h;
		}
		for (int i = 0; i < G[p.v].size(); i++) {
			edge y = G[p.v][i];
			q.push(point(y.to, p.g + y.w, dist[y.to]));
		}
	}
	//没有答案,输出-1
	return -1;
}

int main() {
	int n,m;
	scanf("%d%d", &n, &m);
	while (m--) {
		int a, b, w;
		scanf("%d%d%d", &a, &b, &w);
		G[a].push_back(edge(b, w));
		G2[b].push_back(edge(a, w));
	}
	int s, t, k;
	scanf("%d%d%d", &s, &t, &k);
	if (s == t) k++;  //注意原来就相等时走0步的情况不算一条路径
	dijkstra(t);
	printf("%d\n", A_star(s, t, k));
	return 0;
}

有关问题之外

本题的h(i)函数是需要事先处理的,但也不乏有题目时不需要处理可以直接得出的,例如方格图中的曼哈顿距离,国际象棋棋盘中的对角线距离,不限制方向图中的欧式距离等

相关推荐
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码5 小时前
Cmd命令大全(万字详细版)
python·算法·小程序