以八数码问题为例实现A*算法的求解(未完结)

news2025/4/6 9:16:50

八数码: 

在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
x 4 6
7 5 8

在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1 2 3
4 5 6
7 8 x

例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。

交换过程如下:

1 2 3   1 2 3   1 2 3   1 2 3
x 4 6   4 x 6   4 5 6   4 5 6
7 5 8   7 5 8   7 x 8   7 8 x

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式

输入占一行,将 3×3 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3 
x 4 6 
7 5 8 

则输入为:1 2 3 x 4 6 7 5 8

输出格式

输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出 −1

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19

使用BFS求解思路:

题目要求的是3×3的矩阵八个数字,首先给一个起始状态start,然后要求通过算法,得到题目给的最终状态end。并且要求是最优解,即最短路径。

由此我们可以使用BFS即广度优先算法来解决此类问题,广度优先是层次遍历,每次通过遍历空格位置向上下左右四个方向交换,可得到四个不同的状态(边界可能小于4)以及与初始状态变化距离。

求解存在的问题:用什么数据结构来存储,该如何表示每次变化的状态,该如何记录每次状态距离初始状态的距离。

我们通过队列来记录层次遍历时每层的状态,每一层的共性就是移动距离相同。如图所示:

BFS

由于向左时越界所以跳过向左,由图所示,当前一共有三个状态,并且距离初始状态为1,与end状态比较不相等。

通过上面的解释,我们可以发现,使用队列来记录每次改变的状态最合适,因为队列可以按照层次遍历的顺序来存储状态,可以记录路径和搜索的顺序,并且可以帮助我们避免重复访问。然后,使用哈希表来存储每个状态对应的距离,每次要存储一次距离时,判断哈希表中是否存在当前要存储的状态,然后通过刚刚从队列中取出的状态距离加1即当前状态距离。

每次从队列中获取一个状态都要和最终状态比较,如果相等,返回hash表中对应的距离。否则通过改变空格位置来获取每次状态。如果遍历完所有的状态都没有,返回-1.

这里以JAVA为例,使用BFS算法,展示代码:

import java.sql.Statement;
import java.util.*;

class Main {
    static int dx[] = {1, 0, -1, 0};
    static int dy[] = {0, -1, 0, 1};//空格位置的上下左右移动

    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        String start = new String();
        for (int i = 0; i < 9; i++) {
            char c;
            c = cin.next().charAt(0);
            start += c;
        }
        System.out.println(bfs(start));
    }

    private static int bfs(String start) {
        String end = new String();
        end = "12345678x";
        Queue<String> queue = new LinkedList<>();//存储状态
        Map<String, Integer> map = new HashMap<>();//每个状态到起点的位置
        queue.add(start);
        map.put(start, 0);
        while (!queue.isEmpty()) {
            String s = queue.poll();
            if (s.equals(end)) return map.get(s);
            int dist = map.get(s);
            int k = s.indexOf("x");
            int x = k / 3, y = k % 3;//二维数组中空格的下标
            for (int i = 0; i < 4; i++) {//对空格进行上下左右移动
                int a = x + dx[i], b = y + dy[i];
                if (a >= 0 && a < 3 && b >= 0 && b < 3) {
                    String st = new String(swap(s.toCharArray(), k, a * 3 + b));
                    if (map.get(st) == null) {
                        map.put(st, dist + 1);
                        queue.add(st);
                    }
                    st = new String(swap(s.toCharArray(), k, a * 3 + b));
                }
            }
        }
        return -1;
    }

    private static char[] swap(char[] chars, int k, int i) {
        char temp = chars[k];
        chars[k] = chars[i];
        chars[i] = temp;
        return chars;
    }
}

C++代码:

#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <queue>
using namespace std;
int bfs(string s)
{
	queue<string>q;
	unordered_map<string,int>d;
	q.push(s);
	d[s]=0;
	string end="12345678x";
	int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
	while(!q.empty())
	{
		string t=q.front();
		q.pop();
		
		if(t==end)return d[t];
		int distant=d[t];
		int k=t.find('x');
		int x=k/3,y=k%3;
		for(int i=0;i<4;i++)
		{
			int a=x+dx[i],b=y+dy[i];
			if(a>=0&&a<3&&b>=0&&b<3)
			{	
				
				swap(t[k],t[a*3+b]);
				if(!d.count(t))
				{
					d[t]=distant+1;	
					q.push(t);
				}
			    swap(t[k],t[a*3+b]);
			}
			
		}
	}
	return -1;
}
int main()
{
	string s;
	for(int i=0;i<9;i++)
	{
		char a;
		cin>>a;
		s+=a;
	}
	cout<<bfs(s);
	return 0;
}

 在广度优先遍历算法中,我们发现在找到最终答案之前它遍历了所有状态的八数码(盲目式搜索),对于有明确终点的问题来说,其实可以优先考虑具有较小估计代价的状态来进行搜索以尽快找到解决方案。

 A*算法:

        A*算法(A-star algorithm)是一种启发式搜索算法,常用于解决图形和网络中的最短路径问题。该算法在已知节点之间的连接关系以及每个节点的估计成本(启发函数)的基础上,通过遍历节点来找到从起点到目标节点的最优路径。

        A*算法结合了Dijkstra算法和贪婪最佳优先搜索算法的特点,它维护两个值:g值和h值。其中g值表示起点到当前节点的实际代价,h值表示当前节点到目标节点的预计代价。通过合理选择启发函数,A*算法能够高效地找到最佳路径。

        A*算法使用一个优先队列来存储待探索的节点,并根据f值(f = g + h)的大小进行排序。在每一次迭代中,它选择f值最小的节点进行探索,计算其邻居节点的g值和h值,并更新优先队列。当从队列中选出的节点为目标节点时,路径被找到。

        A*算法在计算资源允许的情况下,通常能够找到最短路径。然而,如果启发函数不准确或者图形/网络过于复杂,A*算法可能会陷入局部最优解并无法找到全局最优解。

A*算法通过下面这个函数来计算每个节点的优先级。

其中:

  • f(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。
  • h(n)是节点n距离终点的预计代价,这也就是A*算法的启发函数。关于启发函数我们在下面详细讲解。

A*算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。

另外,A*算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_setclose_set

A*算法解决八数码问题:

1. 定义状态表示:将八数码问题中的每个状态表示为一个3x3的矩阵,空白格用0表示。例如,初始状态为:

1 2 3 
x 4 6 
7 5 8 


2. 定义启发函数:根据当前状态和目标状态之间的差异,设计一个启发函数来估计从当前状态到目标状态的代价。

3. 实现A*算法:按照以下步骤实现A*算法来解决八数码问题

算法流程图

程序设计语言编写程序,实现算法; 

运行程序并对照分析不同估价函数对A*算法性能的影响。

      

总结

A*算法和广度优先搜索(BFS)都可以用于解决八数码问题,但它们之间存在一些重要的区别。

1. 目标导向性:
    A*算法是一种启发式搜索算法,它使用一个启发函数来估计当前状态到达目标状态的代价。它通过优先考虑具有较小估计代价的状态来进行搜索,以尽快找到解决方案。

   广度优先搜索则是一种朴素的无信息搜索方法,它按照层次遍历的方式搜索问题的解空间,逐渐扩展搜索树,直到找到目标状态为止。BFS不考虑每个状态的代价,只关注解的深度。

2. 内存消耗:
    A*算法通常需要维护一个优先队列来存储待扩展的状态,该队列的大小与搜索过程中生成的状态数量成正比。因此,A*算法可能会占用更多的内存空间。

    广度优先搜索也需要存储搜索树中所有已生成的状态,但不需要额外的优先队列。BFS在搜索空间中逐层扩展,可能会占用较大的内存空间,尤其是当搜索树的分支因子较大时。

3. 时间复杂度:
    A*算法的时间复杂度取决于启发函数的质量。在最坏情况下,A*算法的时间复杂度与状态空间的大小成指数关系。

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

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

相关文章

SQL server数据库端口访问法

最近数据库连接&#xff0c;也是无意中发现了这个问题&#xff0c;数据库可根据端口来连接 网址:yii666.com< 我用的是sql2014测试的&#xff0c;在安装其他程序是默认安装了sql(sql的tcp/ip端口为xxx)&#xff0c;服务也不相同&#xff0c;但是由于比较不全&#xff0c;我…

yum工具(介绍+常用指令)

目录 yum--软件包管理器 概念 引入 rpm安装 yum安装 指令 yum list yum install 包名 yum remove 包名 yum search 包名/关键字 yum update/upgrade yum makecache fast du命令 yum--软件包管理器 概念 引入 在linux中下载软件,可以下载源代码进行编译执行但是…

使用Ansible中的playbook

目录 1.Playbook的功能 2.YAML 3.YAML列表 4.YAML的字典 5.playbook执行命令 6.playbook的核心组件 7.vim 设定技巧 示例 1.Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 2.YAML #简介# 是一种表达资料序列的格式,类似XML #特…

GB28181学习(十四)——语音广播与语音对讲

语音对讲 定义 用户端向设备通过视音频点播请求音频数据&#xff1b;用户端接收音频数据并通过特定的播放设备&#xff08;如音响&#xff09;播放&#xff1b;用户端向设备发送广播请求&#xff1b;设备解析广播成功后通过INVITE方法向用户请求音频数据&#xff1b;用户通过音…

大长案例 - 经典长连接可水平扩容高可用架构

文章目录 需求设计 需求 支撑百万充电桩充电业务的长连接可水平扩容高可用架构需求如下&#xff1a; 可扩展性&#xff1a;系统应该具备高度可扩展性&#xff0c;能够轻松应对新增充电桩的需求。任何时候都应该容易添加更多的充电桩&#xff0c;而不会影响整体性能。 负载均衡…

Linux C语言进阶-D7~D8指针与数组

一维数组 一维数组的数组名&#xff1a;就是一维数组的指针&#xff08;起始地址&#xff09; 如果int *px x;则&#xff1a; x[i]、*(pxi)、*(xi)、px[i]具有完全相同的功能&#xff1a;访问数组第i1个数组元素 注意&#xff1a;1、指针变量和数组在访问数组元素时&#x…

筛网孔径测量方法,您了解多少?

筛网是一种表面有均匀而稳定的透气孔、具有筛选和过滤作用的工业用品&#xff0c;常见的有金属丝编织网和冲孔板筛网&#xff0c;广泛用于新能源汽车、太阳能光伏、冶金、煤炭、橡胶、石油、化工、制药、建材、粮油等行业。 筛网有着严格的网孔尺寸规范&#xff0c;需要符合标…

141. 环形链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

安装opensips

1. 安装opensips ubuntu下安装&#xff1a; 1&#xff09;执行以下的脚本 openSIPS | APT Repository 2&#xff09;apt-get install opensips 安装完毕后&#xff0c;再选择需要的module继续安装&#xff0c;不需要编译 如果只是用作load balancer&#xff0c;那么只需要…

剑指JUC原理-8.Java内存模型

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

Windows安装tensorflow-gpu=1.14.0CUDA=10.0cuDNN=7.4 (多版本CUDA共存)

文章目录 0. 前置说明1. 查看版本对应关系2. 安装 cuda3. 安装 cudnn4. 添加环境变量5. 安装 tensorflow 0. 前置说明 本机&#xff08;Windows 11&#xff09;已安装CUDA 11.7 使用命令查看显卡驱动&#xff1a; nvidia-smi这里显示的CUDA Version: 11.7说明支持安装11.7版本…

小说网站源码带管理后台手机端和采集

搭建教程&#xff0c;安装宝塔 php7.2&#xff0c;绑定域名&#xff0c;上传源码到根目录解压 源码获取请自行百度&#xff1a;一生相随博客 一生相随博客致力于分享全网优质资源&#xff0c;包括网站源码、游戏源码、主题模板、插件、电脑软件、手机软件、技术教程等等&#…

2.Docker的安装

1.认识Docker的基本架构 下面这张图是docker官网上的&#xff0c;介绍了整个Docker的基础架构&#xff0c;我们根据这张图来学习一下docker的涉及到的一些相关概念。 1.1 Docker的架构组成 Docker架构是由Client(客户端)、Docker Host(服务端)、Registry(远程仓库)组成。 …

每天一道算法题:11. 盛最多水的容器

难度 中等 题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 **说明&#xff1a;**你不…

哈夫曼树c语言版

一、哈夫曼树概念 哈夫曼树又称最优树给定N个权值作为N个叶子结点&#xff0c;构造一棵二叉树&#xff0c;若该树的带权路径长度达到最小&#xff0c;称这样的二叉树为最优二叉树&#xff0c;也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树&#xff0c;权值较大…

传输层协议——TCP协议 (详解!!!)

目录 TCP的报文格式 1. 源端口号&#xff0c;目的端口号 和 udp 相同&#xff08;前面文章介绍了udp&#xff09; 2. 4位首部长度 —— TCP的报头长度 3. 选项 —— option &#xff08;可选的&#xff1a;可以有&#xff0c;可以没有&#xff09; 4.保留&#xff08;6…

linux下mysql-8.2.0集群部署(python版本要在2.7以上)

目录 一、三台主机准备工作 1、mysql官方下载地址&#xff1a;https://dev.mysql.com/downloads/ 2、修改/etc/hosts 3、关闭防火墙 二、三台主机安装mysql-8.2.0 1、解压 2、下载相应配置 3、初始化mysql&#xff0c;启动myslq&#xff0c;设置开机自启 4、查看初始密…

java基础--多线程学习

写在前面&#xff1a; 多线程在面试中问的很多&#xff0c;之前没有过系统的学习&#xff0c;现在来进行一个系统的总结学习 文章目录 基础java多线程实现无参无返回值线程快速创建start和run方法的探讨run方法线程状态 有返回值线程线程池执行小结关于抛出异常的扩展 线程方…

人脸检索 M:N(视频,摄像头),调用百度API

目录 创建百度智能云账号 代码部分&#xff1a; 创建百度智能云账号 网址&#xff1a; 百度智能云-云智一体深入产业 点击导航栏中的产品&#xff0c;再选择人脸与人体 再选择人脸搜索 进入后&#xff0c;可以先去领取免费资源&#xff0c;如果不领取&#xff0c;后面是无法…

macOS Sourcetree 选择文件比较工具 Kaleidoscope

Sourcetree 选择文件比较工具 Kaleidoscope Kaleidoscope 使用的命令行工具是 ksdiff。Sourcetree 集成 Kaleidoscope之前&#xff0c;必须先安装 ksdiff。 Kaleidoscope 使用的命令行工具是 ksdiff。Sourcetree 集成 Kaleidoscope之前&#xff0c;必须先安装 ksdiff。 打开Ka…