首页 > 编程语言 >【笔记/模板】A*算法

【笔记/模板】A*算法

时间:2024-11-03 14:58:58浏览次数:3  
标签:dist int ed 笔记 st 算法 heap 模板

A* 算法

定义

A* 搜索算法(\(\text{A*search algorithm}\))是一种在图形平面上,对于有多个节点的路径求出最低通过成本的算法。它属于图遍历(英文:\(\text{Graph traversal}\))和最佳优先搜索算法(英文:\(\text{Best-first search}\)),亦是 BFS 的优化,用到了启发式搜索的思维。

启发式搜索(英文:\(\text{heuristic search}\))是一种在普通搜索算法的基础上引入了启发式函数的搜索算法。

启发式函数的作用是基于已有的信息对搜索的每一个分支选择都做估价,进而选择分支。简单来说,启发式搜索就是对取和不取都做分析,从中选取更优解或删去无效解。

过程

确定了起点 \(st\) 和终点 \(ed\),对于每个点 \(x\),计算出从 \(st\) 开始的距离函数 \(g(x)\),到 \(ed\) 的预估代价距离函数 \(h(x)\) 和实际代价函数 \(h^*(x)\),不难推出每个点上的估价函数为:

\[f(x) = g(x) + h(x) \]

A* 算法的正确性在保证对于任意一点 \(x\),都有 \(h \le h^*\)

类似 Dijikstra 算法,A* 算法每一次从优先队列中取出一个 \(f\) 值最小的元素并以此更新相邻的所有状态。

值得注意的是:当 \(h = 0\) 时,A* 算法退化为 Dijkstra 算法,当 \(h = 0\) 且边权为 \(1\) 时,会演变为 BFS。

K 短路

题目

按顺序求一个有向图上从结点 \(st\) 到结点 \(ed\) 的所有路径最小的前任意多(不妨设为 \(k\))个。

解题思路

不难发现,这是很容易转化成 A* 算法的。

初始状态设为 \(st\),重点为 \(ed\),距离函数 \(g(x)\) 表示从 \(st\) 到 \(x\) 点的实际距离,估价函数 \(h(x)\) 表示从当前点到节点 \(ed\) 的估计距离。

通过一次反向的建图跑一边 Dijkstra,计算出终点 \(ed\) 到所有点的最短路,然后将初始状态依次加入到队列当中,每次取出 \(f\) 值最小的一项,计算出相邻的所有点并全部加入到队列当中。当第 \(k\) 次走到节点 \(ed\) 时,便是所得到的答案。

优化:当我们第 \(k + 1\) 走到此节点时,直接跳过该状态。

参考代码

// Problem: 第K短路
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/180/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

using namespace std;

// #define int long long

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;

const int N = 1e4 + 9;
const int INF = 0x3f3f3f3f;

int n, m, st, ed, k; 
int dist[N], cnt[N];

struct Edge
{
	int u, w;			// 出点,权值
	bool operator < (const Edge &u) const { return w > u.w; }
};						// 存边用的结构体

vector<Edge> g[N];		// 正向建图
vector<Edge> rg[N];		// 反向建图

struct Node
{
	int f, g, idx;		// 估价值 + 真实值,真实值,编号
	bool operator<(const Node &u) const { return f > u.f; }
};

void dijkstra()			// 求出终点到起点时扩展过的所有点的路(被用来当作估价函数,因为其值不会小于实际距离)
{
	priority_queue<Edge> heap;
	bitset<N> vis;
	memset(dist, 0x3f, sizeof dist);

	heap.push({ed, 0});
	dist[ed] = 0;
	
	while (!heap.empty())
	{
		auto t = heap.top(); heap.pop();

		int u = t.u;
		if (vis[u]) continue;
		vis[u] = true;

		for (auto y : rg[u])
		{
			int v = y.u, w = y.w;
			if (dist[v] > dist[u] + w)
			{
				dist[v] = dist[u] + w;
				heap.push({v, dist[v]});
			}	
		}
	}
}

int astar()
{
	priority_queue<Node> heap;
	heap.push({dist[st], 0, st});	// 初始点到终点的f值为dist[st] + 0,起点到自身的距离为 0
	
	while (!heap.empty())
	{
		auto t = heap.top(); heap.pop();
		
		int idx = t.idx, distance = t.g;	// 取出编号和st点到该点的实际距离
		cnt[idx] ++;	// 小优化
		if (cnt[ed] == k) return distance;	// 如果终点遍历了 k 次,直接return掉
		
		for (auto y : g[idx])
		{
			int v = y.u, w = y.w;
			if (cnt[v] < k)
				heap.push({distance + w + dist[v], distance + w, v});	// 该状态仍可扩展
		}
	}
	return -1;	// 无解时直接 return
}

signed main()
{
	// ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= m; i ++)
	{
		int u, v, w; cin >> u >> v >> w;
		g[u].push_back({v, w});			// 正向建图
		rg[v].push_back({u, w});		// 反向建图
	}
	
	cin >> st >> ed >> k;
	if (st == ed) k ++;					// 题目要求,至少要有一条路
	
	dijkstra();
	
	// cout << dist[st] << '\n';
	// if (dist[st] == INF) cout << -1 << '\n';
	cout << astar() << '\n';			// 输出 k 短路
	
	return 0;
}

标签:dist,int,ed,笔记,st,算法,heap,模板
From: https://www.cnblogs.com/ThySecret/p/18523437

相关文章

  • 【笔记/模板】Bellman-Ford
    Bellman-Ford求最短路和负环时间复杂度:\(O(nm)\)【模板/笔记】Johnson算法boolBellman_Ford(){memset(dist,0x3f,sizeofdist);for(intk=1;k<n;k++)for(intver=1;ver<=n;ver++)for(inti=h[ver];~i;i=ne[i])......
  • 【模板】缺省源
    Debuginlinevoiddebug(){cerr<<'\n';}template<typenameType,typename...Other>inlinevoiddebug(constType&x,constOther&...y){cerr<<x<<'';debug(y...);}#defineDEBUG(a...)cerr<......
  • 【模板】手写基本数据结构
    栈STL模板STL中的stack容器提供了一众成员函数以供调用,常见的包括但不限于如下:创建一个栈:stack<int>stk;修改元素:stk.push(x);将传入的参数插入到栈顶。stk.pop();将栈顶的元素弹出。查询:stk.top();返回栈顶的元素。stk.empty();返回栈是否为空。stk.size......
  • 【模板】素数筛
    模板原题1.寻找因数筛法时间复杂度:\(O(n\sqrtn)\)核心模板代码如下:boolisprime(intn){ if(n<2)returnfalse; //0和1都不是 for(inti=2;i*i<=n;++i) if(n%i==0)returnfalse; //有1和它本身以外的其他因子就不是素数了 returntrue;}2.埃......
  • 【Python】深入解析Python中的多重继承与MRO:原理、C3线性化算法与super()用法
    解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界Python的多重继承机制允许一个类从多个父类中继承属性和方法,这带来了极大的灵活性和复用性,但也引发了“菱形继承”问题,即多条继承路径导致同一属性或方法重复调用。为了解决此问题,Python引入了MRO(方法解析顺序)规......
  • C++学习笔记
    一、从C转向C++1.1、使用const和inline而不#define使用constconstintm=10;intn=m;上述代码在C中,编译器会先到m的内存中取出数据,再赋值给n;但在C++中,会直接将10赋值给n。C++的常量更类似于#define,是一个替换过程。#define经常不被认为是语言的一部分。define本......
  • 【综合算法学习】(第十五篇)
    目录图像渲染(medium)题目解析讲解算法原理编写代码岛屿数量(medium)题目解析讲解算法原理编写代码图像渲染(medium)题目解析1.题目链接:.-力扣(LeetCode)2.题目描述有⼀幅以mxn的⼆维整数数组表⽰的图画image,其中image[i][j]表⽰该图画的像素值⼤⼩。你也被给予三......
  • 蓝桥算法
    1.https://www.lanqiao.cn/problems/19954/learning/?contest_id=214这道题用快速幂直接秒,而快速幂就是求一个数的次方很大的时候,我们可以把指数分解为二进制的形式,再有a的b*c次方等于a的b次方乘以a的c次方,在用一个数存储一下即可。代码如下:defqui(x,y):res=1whiley:if......
  • 【算法-选择排序】挑挑拣拣,排出顺序——选择排序入门
    什么是选择排序?选择排序是一种比较简单直接的排序方式。想象你在打散一副牌,想按照大小顺序从小到大排列这些牌。你会怎么做?可能会先找出最小的那张,放在最前面,然后在剩下的牌里找第二小的,依次类推,这就是选择排序的基本思路!在程序中,选择排序的操作流程也类似:它逐步将未排序......
  • 《AI 算法的突破与挑战:探寻人工智能的核心驱动力》
    在当今科技飞速发展的时代,AI算法无疑是人工智能领域的核心驱动力,它的不断演进和突破正在重塑我们的世界。从简单的代码到如今令人惊叹的“智能大脑”,AI算法经历了漫长的发展历程,取得了诸多令人瞩目的成就,但同时也面临着一系列的挑战。一、AI算法的辉煌成就精度超越......