《算法竞赛进阶指南》0x27A*


如果给定一个“目标状态”,需要求出从初态到目标状态的最小代价,那么优先队列BFS的“优先策略”显然不完善。一个状态的当前代价最小,只能说明从起始状态到当前状态得到代价很小,而在未来的搜索中,从该状态到目标状态可能会花费很大的代价。另外。一些状态虽然虽然当前代价略大,但是未来到目标状态的代价可能会很小,于是从起始状态到目标状态的总代价反而会更优。

为了提高搜索效率,我们很自然想到,可以对未来可能产生的代价进行预估。详细地讲骂我们可以设计一个 ”估价函数“,以任意状态为输入,计算从该状态到目标状态所需代价的估计值。在搜索中,仍维护一个堆,不断从堆中取出 “当前代价+未来估价”最小的状态进行扩展。

这种带有估价函数的优先队列BFS就是A*算法。只要保证对于任意状态state,都有f(state) ≤ \leq g(state),A*算法就一定能在目标状态第一次从堆中被取出时得到最优解,但是搜索过程中每个状态可能被扩展多次,估价f(state)越准确,越接近g(state),A*算法的效率越高。若估价值始终为0,就等价于普通的优先队列BFS。

总结
A* 应用场景:
起点→终点的最短距离
状态空间 >> 1e10
启发函数减小搜索空间

A*算法:
while(q.size())
t ← 优先队列的队头 小根堆
当终点第一次出队时 break;
从起点到当前点的真实距离 d_real
从当前点到终点的估计距离 d_estimate
选择一个两距离之和最小的点
for j in ne[t]:
将邻边入队

A*算法条件:
估计距离<=真实距离
d[state] + f[state] = 起点到state的真实距离 + state到终点的估计距离=估计距离
^
d[state] + g[state] = 起点到state的真实距离 + state到终点的真实距离=真实距离

一定是有解才有 d[i] >= d[最优] = d[u]+f[u]
f[u] >= 0

证明终点第一次出队列即最优解

1 假设终点第一次出队列时不是最优 则说明当前队列中存在点u有 d[估计]< d[真实]d[u] + f[u] <= d[u] + g[u]<d[队头终点]即队列中存在比d[终点]小的值,
2 但我们维护的是一个小根堆,没有比d[队头终点]小的d[u],矛盾证毕

A* 不用判重
以边权都为1为例
A o→o→o
↑ ↓
S o→o→o→o→o→o→o T
B
dist[A] = dist[S]+1 + f[A] = 7
dist[B] = dist[S]+1 + f[B] = 5
则会优先从B这条路走到T
B走到T后再从A这条路走到T

例题
acwing178.第K短路
首先可以使用数学归纳法得到一个结论:对于任意正整数i和目标节点x,当第i次从堆中取出包含节点x的二元组时,对应的dist值就是从S到x的第k短路。
使用优先队列BFS在最坏条件下时间复杂度为 O ( K ∗ ( N + M ) ∗ l o g ( N + M ) ) O(K*(N+M)*log(N+M)) O(K(N+M)log(N+M)),这道题给定了起点和终点,可以考虑使用A*提高搜索效率。
我们把估价函数f(x)定为从x到T的最短路长度,这样不但保证了f(x) ≤ \leq g(x),还能顺应g(x)的实际变化趋势。

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int> PII;
typedef pair<int,pair<int,int>> PIII;
#define MAX_N 1000
#define MAX_M 10000
int n,m;
int h1[MAX_N+5],h2[MAX_N+5];
int e[MAX_M*2+5],ne[MAX_M*2+5],w[MAX_M*2+5];
int v[MAX_N+5],dist[MAX_N+5];
int S,T,K;
int cnt=0,tot=0;
void add(int h[],int a,int b,int c)
{e[++tot]=b;w[tot]=c;ne[tot]=h[a];h[a]=tot;
}
void dijkstra()
{memset(dist,0x3f,sizeof dist);priority_queue<PII,vector<PII>,greater<PII>>heap;heap.push({0,T});dist[T]=0;while(heap.size()){PII t=heap.top();heap.pop();if(v[t.second])continue;v[t.second]=1;for(int i=h2[t.second];i;i=ne[i]){if(dist[e[i]]>dist[t.second]+w[i]){dist[e[i]]=dist[t.second]+w[i];heap.push({dist[e[i]],e[i]});}}}return ;
}
int Astar()
{priority_queue<PIII,vector<PIII>,greater<PIII>>heap;heap.push({dist[S],{0,S}});if(dist[S]==0x3f3f3f3f)return -1;while(heap.size()){PIII t=heap.top();int distance=t.second.first;int ver=t.second.second;heap.pop();if(ver==T)cnt++;if(cnt==K)return distance;for(int i=h1[ver];i;i=ne[i]){heap.push({distance+w[i]+dist[e[i]],{distance+w[i],e[i]}});}}return -1;
}
int main()
{cin>>n>>m;for(int i=0,a,b,c;i<m;i++){cin>>a>>b>>c;add(h1,a,b,c);add(h2,b,a,c);}cin>>S>>T>>K;if(S==T)K++;dijkstra();cout<<Astar()<<endl;return 0;
}

acwing179.八数码
先进行可解性判定。把除空格以外的所有数字排成一个序列,求出该序列的逆序对数。如果初态和终态的逆序对数的奇偶性相同,那么这两个状态可以相互到达,否则一定不能到达。
若问题有解,我们可以使用A*搜索一种移动步数最少的方案,我们把估计函数定为所有数字在state中的对应位置与目标状态end中的位置的曼哈顿距离之和。

#include<iostream>
#include<unordered_map>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,string> PIS;
string start,ed,num;
int dx[]={-1,1,0,0},dy[]={0,0,-1,1};
int cnt=0;
string match="udlr";
int f(string s)
{int ans;for(int i=0;i<9;i++){if(s[i]=='x')continue;int t=s[i]-'1';ans+=abs(i/3-t/3)+abs(i%3-t%3);}return ans;
}
string Astar()
{unordered_map<string,int>dist;unordered_map<string,pair<char,string>>prev;priority_queue<PIS,vector<PIS>,greater<PIS>>heap;heap.push({f(start),start});dist[start]=0;while(heap.size()){PIS t=heap.top();heap.pop();string state=t.second;if(state==ed)break;int x,y;for(int i=0;i<9;i++)if(state[i]=='x'){x=i/3;y=i%3;}string memory=state;for(int i=0;i<4;i++){int a=x+dx[i],b=y+dy[i];if(a<0||a>2||b<0||b>2)continue;swap(state[x*3+y],state[a*3+b]);if(!dist.count(state)||dist[state]>dist[memory]+1){dist[state]=dist[memory]+1;prev[state]={match[i],memory};heap.push({dist[state]+f(state),state});}state=memory;}}string ans;while(ed!=start){ans=prev[ed].first+ans;ed=prev[ed].second;}return ans;
}
int main()
{char ch;for(int i=0;i<9;i++){cin>>ch;start+=ch;if(ch!='x')num+=ch;}ed="12345678x";for(int i=0;i<8;i++)for(int j=i+1;j<9;j++)if(num[i]>num[j])cnt++;if(cnt&1)cout<<"unsolvable"<<endl;else cout<<Astar()<<endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/51440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓好软-----手机端提取apk的小工具 方便简单 无需root权限

apk提取工具 工具小巧。可以提取手机上面当前安装的apk和系统应用apk。而且无需root权限即可正常使用。 效果非常不错。比其他工具提取系统app方便好使。 下载&#xff1a;https://download.csdn.net/download/mg668/89683199?spm1001.2014.3001.5503

探索贪心算法:解决优化问题的高效策略

贪心算法是一种在每一步选择中都采取当前最佳选择的算法&#xff0c;以期在整体上达到最优解。它广泛应用于各种优化问题&#xff0c;如最短路径、最小生成树、活动选择等。本文将介绍贪心算法的基本概念、特点、应用场景及其局限性。 贪心算法的基本概念 贪心算法的核心思想是…

详解ACL限制SSH、Telnet远程登录及抓包实验

要求&#xff1a;lsw5只能lsw6登录&#xff0c;lsw6只能PC2登录 <Huawei>sys [Huawei]sysname sw2 [sw2]int vlanif1 [sw2-Vlanif1]ip address 192.168.10.2 24 [sw2-Vlanif1]q [sw2] <Huawei>sys [Huawei]sysname sw1 [sw1]int vlanif1 [sw1-Vlanif1]ip address …

动态代理IP的适用范围与优势分析

1. 什么是动态代理IP&#xff1f; 动态代理IP是一种在每次连接时更换IP地址的代理服务。与静态代理IP不同&#xff0c;动态代理IP会在每次访问时分配一个新的IP地址&#xff0c;或在设定的时间间隔内自动更换。这种机制使得动态代理IP非常适合需要频繁更换IP的应用场景。 2. …

视频单条剪、脚本靠手写?云微客开启海量视频时代

老板们注意了&#xff0c;现在已不再是视频单条剪&#xff0c;脚本靠手写的时代&#xff01;在这个信息爆炸的时代&#xff0c;短视频已经成为了现代信息传播和娱乐消费的重要载体&#xff0c;那么我们该如何高效、快速地制作出大量高质量的短视频内容呢&#xff1f;这就需要云…

面试必备:接口自动化测试精选面试题大全

一、 请问你是如何做接口测试的&#xff1f; 大体来说&#xff0c;经历以下过程&#xff1a;接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说&#xff0c;接口测试流程分成以下九步&#xff1a; 第一步&am…

SpringBoot集成kafka开发-消息消费的分区策略(消费者如何判断从哪个分区中消费消息的?)

这里写目录标题 1、kafak消息者消费消息的4种分区策略2、kafka默认的消费分区策略1-RangeAssignor&#xff08;均匀分配、默认分配策略&#xff09;2.1、代码验证RangeAssignor的消息分区策略2.1.1、消费者2.1.2、生产者2.1.3、kafak配置类2.1.4、对象实体类2.1.5、项目配置文件…

基于vue框架的病床管理信息系统odt4v(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 开题报告内容 基于Vue框架的病床管理信息系统开题报告 一、引言 随着医疗技术的不断进步和人口老龄化的加剧&#xff0c;医院面临着日益增长的医疗服务需求。病床作为医院资源的重要组成部分&#xff0c;其管理效率直接影响到患者的就医体验和医院的运营效…

Elasticsearch(面试篇)

目录 Elasticsearch的倒排索引是什么&#xff1f; 详细描述一下Elasticsearch更新和删除文档的过程 描述一下Elasticsearch搜索的过程 兄弟们一起加油 &#xff01; &#xff01; &#xff01; Elasticsearch的倒排索引是什么&#xff1f; 传统我们索引通过文章&#xff0c…

TCP/UDP的对比,粘包分包抓包,http协议

服务器端&#xff1a; 一、loop 127.0.0.1本地回环测试地址 二、tcp特点 面向连接、可靠传输、字节流 粘包问题&#xff1a;tcp流式套接字&#xff0c;数据与数据之间没有套接字&#xff0c;导致可能多次的数据粘到一起 解决方法&#xff1a;&#xff08;1&#xff09;规…

使用USB转485转接器,从机不响应

在调试485通信时&#xff0c;bms不响应主机&#xff0c;usb转485转换器接收指示灯常亮&#xff0c;现象如下&#xff0c;通过更新驱动程序解决。 更新驱动程序链接&#xff1a; 1. 下载并解压驱动文件 https://www.szutek.com/Uploads/file/20210917/20210917091627_42822.rar…

生信圆桌:专业生信服务器与平台服务的提供者

生信圆桌是一个专注于提供生物信息学&#xff08;生信&#xff09;服务器和平台服务的领先企业&#xff0c;致力于为全球科研机构、企业和独立研究者提供高性能的生信分析解决方案。随着生物信息学研究对计算资源的需求日益增加&#xff0c;生信圆桌凭借其先进的服务器技术和专…

C++ 多线程(互斥锁、条件变量)

互斥锁 最简单示例&#xff1a; #include <iostream> #include <thread> #include <mutex>std::mutex mtx; int counter 0;void increment() {for (int i 0; i < 10000; i) {// b1 是创建出来的对象. lock_guard 类似智能指针一样,为了防止忘记释放锁…

【QT学习】1-2 Liunx环境下QT5.12.9软件安装1——VMware17.0.0虚拟机安装

注意&#xff1a;如果电脑已经安装低版本的VMware&#xff0c;千万不要卸载&#xff0c;直接覆盖安装&#xff0c;更新到新的安装版本 1.点击.exe文件&#xff0c;右键以管理员身份运行&#xff0c;点击下一步&#xff0c;下一步 2.选择软件安装位置后&#xff0c;点击下一步。…

手写Spring框架(简单实现)

Spring风格的三级缓存管理&#xff1a;一个简单实现示例 在实现一个简单的Spring风格的依赖注入容器时&#xff0c;我们通常需要处理Bean的生命周期&#xff0c;包括实例化、属性注入以及完全初始化。为了处理复杂的依赖关系和循环依赖问题&#xff0c;我们可以使用三级缓存机…

虚幻5|制作一个木桩,含血量及伤害数字

一&#xff0c;基础设置 1.创建Actor蓝图类 2.编辑胶囊体和网格体的碰撞预设 3.打开敌人的角色蓝图&#xff0c;编辑飙血特效 二&#xff0c;创建敌人血量的ui&#xff0c;命名为敌人血量&#xff0c;如下 1. 2&#xff0c;打开后&#xff0c;添加一个画布画板和进度条&#…

ArkUI-布局(一)

ArkUI-布局 布局概述布局元素的组成如何选择布局布局位置对子元素的约束 线性布局部分属性与使用方式 层叠布局部分属性与使用方式 弹性布局基本概念布局方向布局换行主轴对齐方式交叉轴对齐方式容器设置交叉轴对齐方式子元素设置交叉轴对齐方式内容对齐 自适应拉伸 布局概述 …

C++基础面试题 | 什么是C++的列表初始化?

青山相待&#xff0c;白云相爱&#xff0c;梦不到紫罗袍共黄金带。 - 《山坡羊道情》(宋方壶) 2024.8.26 回答重点 C11中引入了列表初始化&#xff0c;其语法是用花括号{}来初始化变量或对象。列表初始化可以应用于&#xff1a; 基础数据类型用户自定义类型&#xff08;类、结…

云端集中管控边缘服务:利用 EMQX ECP 在 K8s 上快速部署 NeuronEX

随着物联网、边缘计算技术的发展&#xff0c;实现边缘服务的快速部署对于分布式计算环境至关重要。它不仅可以显著降低延迟、节省带宽资源、增强数据的安全性和隐私保护&#xff0c;同时还能改善用户体验&#xff0c;支持动态变化的工作负载需求&#xff0c;提供更高的灵活性和…

Linux 实时调度器:带宽限制

文章目录 1. 前言2. 概念3. 实时进程 的 带宽限制3.1 实时进程 带宽限制 初始化3.2 启动 实时进程 带宽 监测定时器3.3 累加 实时进程 消耗的带宽3.4 查看 实时进程 带宽消耗情况3.5 小结 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的…