【BFS】八数码问题(c++基础算法)

news2025/2/3 19:03:17

目录

一.读题

二.在做题之前

1.康拓展开

2.DFS和BFS的区别

3.栈和队列的区别

三.做题

1.算法原理

2.算法实现

①队列

②康托展开

 ③标记

四.AC代码


一.读题

作为最经典的一道宽度优先搜索题,它的题面并不是很难懂。

【宽搜(难度:6)】8数码问题

题目描述

【题意】 
  

在3×3的棋盘上摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围上下左右相邻的棋子可以移到空格中。

现给出原始状态和目标状态,求实现从初始布局到目标布局的最少步骤(初始状态的步数为0)。

如下图,答案为5。 

  


 
【输入格式】
    第一个3*3的矩阵是原始状态;
    第二个3*3的矩阵是目标状态。
【输出格式】
    输出移动所用最少的步数。
【样例1输入】
2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5
【样例1输出】
5
 
【样例2输入】
2 8 3
1 6 4
7 0 5
0 1 2
3 4 5
8 7 6
【样例2输出】
17

很显然,这是要我们求出矩阵1通过白色方块的上下左右移动转化向矩阵2的最小步数。


二.在做题之前

在做题之前,我们先要弄懂3个问题。

1.康拓展开

在这道题中,我们要利用康托展开判断是否重复。在文前,蒟蒻已经写了一篇文章,不懂的可以去看一下:【宽搜必备】康托展开(从公式解析到代码实现)

那么,我们就可以写出:

int kt(int a[],int n)
{
	int s=1;
	for(int i=1;i<=n;i++)
	{
		int index=1,f=1,count=0;
		for(int j=i+1;j<=n;j++)
		{
			f*=index;
			index++;
			if(a[i]>a[j]) count++; 
		}
		s=s+count*f;
	}	
	return s;
} 

2.DFS和BFS的区别

bfs 遍历节点是先进先出,dfs遍历节点是先进后出
bfs是按层次访问的,dfs 是按照一个路径一直访问到底,当前节点没有未访问的邻居节点时,然后回溯到上一个节点,不断的尝试,直到访问到目标节点或所有节点都已访问。
bfs 适用于求源点与目标节点距离最近的情况,例如:求最短路径。dfs 更适合于求解一个任意符合方案中的一个或者遍历所有情况,例如:全排列、拓扑排序、求到达某一点的任意一条路径。

3.栈和队列的区别

(1)栈和队列的出入方式不同:栈是后进先出、队列是先进先出
(2)栈和队列在具体实现的时候操作的位置不同:因为栈是后进先出,它在一段进行操作;而队列是先进先出,实现的时候在两端进行。

现在,我们搞懂了这三个问题,就可以做题了。


三.做题

1.算法原理

采用BFS遍历的方式寻找最优路径。

首先定义一个结构体ma来存放八数码的每一个状态信息,其中包括节点对应的矩阵,节点在BFS遍历树中的深度(相当于步数),以及节点对应的康托值。然后,定义visited数组存放已经访问过的节点状态。

利用队列实现遍历,具体步骤如下:

1.将初始状态的各种信息压入队列中。
2.判断队列是否为空,若为空,退出循环,打印移动步骤,结束。
3.取出队头元素判断是否与目标状态一致。若一致,则退出循环,输出移动步骤,程序结束。若不一致,则分别判断空格向左、向上、向下以及向右能否移动。                                                                 5.若可以移动,求其康托值,然后压进队列。并跳转到步骤四。

转载图,侵权必删

2.算法实现

①队列

因为此队列要存的东西是一个结构体,因此也要把其类型定为结构体ma

②康托展开

在此代码中,康托展开用于判重。要将一个3*3的矩阵换为一个数。首先,我们要把此二维数组变为一维的。

    int d[10],len = 0;
    for (int i = 1; i <= 3; i++)
    {
        for (int j = 1; j <= 3; j++) 
        {
            d[++len] = ak.a[i][j];
        }
    }

然后,进行康拓转化。最后就是这样

int kt(ma ak)
{
    int d[10],len = 0;
    for (int i = 1; i <= 3; i++)
    {
        for (int j = 1; j <= 3; j++) 
        {
            d[++len] = ak.a[i][j];
        }
    }
    int s=1;
    for(int i=1;i<=9;i++)
    {
        int index=1,f=1,count=0;
        for(int j=i+1;j<=9;j++)
        {
            f=f*index,index++;
            if(d[i]>d[j]) count++;
        }
        s=s+count*f;
    }
    return s;
}

 ③标记

很简单,用数组flag标记康托值即可


四.AC代码

#include<bits/stdc++.h>
using namespace std;
struct ma{
    int a[10][10],x0,y0,ans,kt; 
};
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
queue<ma>q;
bool flag[400000];
int kt(ma ak)
{
    int d[10],len = 0;
    for (int i = 1; i <= 3; i++)
    {
        for (int j = 1; j <= 3; j++) 
        {
            d[++len] = ak.a[i][j];
        }
    }
    int s=1;
    for(int i=1;i<=9;i++)
    {
        int index=1,f=1,count=0;
        for(int j=i+1;j<=9;j++)
        {
            f=f*index,index++;
            if(d[i]>d[j]) count++;
        }
        s=s+count*f;
    }
    return s;
}
int main()
{
    ma shi,mo;
    for(int i=1;i<=3;i++)
    {
        for(int j=1;j<=3;j++)
        {
            scanf("%d",&shi.a[i][j]);
            if(shi.a[i][j]==0)
            {
                shi.x0=i,shi.y0=j;
            }
        }
    }
    shi.ans = 0;
    shi.kt = kt(shi);
    flag[shi.kt] = 1;
    q.push(shi);
    for(int i=1;i<=3;i++)
    {
        for(int j=1;j<=3;j++)
        {
            scanf("%d",&mo.a[i][j]);
        }
    }
    mo.kt=kt(mo);
    while(!q.empty())//q非空,可以走
    {
        for(int i=0;i<4;i++)//四个方向
        {
            ma ac=q.front();
            int nx = ac.x0 + dx[i];
            int ny = ac.y0+ dy[i];
            if(nx>=1&&ny>=1&&nx<=3&&ny<=3)
            {
                swap(ac.a[ac.x0][ac.y0],ac.a[nx][ny]);
                ac.x0=nx;
                ac.y0=ny;
                //将0与目标数交换
                ac.ans++;//步数加1
                ac.kt=kt(ac);
                //康托判重
                 if (!flag[ac.kt])
                {
                    flag[ac.kt] = 1;
                    q.push(ac);
                    //加入队列
                    if(ac.kt==mo.kt)
                    { 
                        printf("%d",q.back().ans);
                        exit(0);
                    }
                }
            }
        }
        q.pop();
        //弹出已遍历完所有情况的矩阵
    }
}

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

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

相关文章

爆款短视频拍摄技巧之摇、移、跟拍等,这样拍的视频才更有吸引力,速收藏

爆款短视频拍摄技巧之拍摄手法,这样拍的视频才更有吸引力。 拍摄技巧主要分为两个部分&#xff0c;一个是构图&#xff0c;一个是拍摄手法。上一篇我们聊过了两种构图手法&#xff0c;接下来咱们聊一下拍摄手法&#xff0c;也就是我们常说的推、拉、摇、移、跟这五种手法。 其…

NNDL 2022秋

第一届AI专业&#xff0c;很多课程都是第一次开课&#xff0c;老师和学生都在“摸着石头过河”。 好处是所学内容比较新&#xff0c;跟得上“潮流”&#xff0c;学习意愿比较强。 难处是教学资料相对欠缺&#xff0c;需要学的内容较多&#xff0c;难度较大。 大家经过一学期…

leetcode:1494. 并行课程 II【dfs记忆化 + 状态压缩】

目录题目截图题目分析ac code总结题目截图 题目分析 这道题很像toposort&#xff0c;但实际不是因为那些indeg为先为0的&#xff0c;先选不一定好的考虑到n很小我们使用状态压缩dfs(state)表示当前state下&#xff0c;还需要多少个学期结束用pre数组存一下每个idx对应的前置条…

基于C++11 实现的线程池

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 基于C11 实现的线程池基于C11 实现的线程池1、线程池原理2、线程池的设计思路&#xff1f;3、一个基于C11的优秀的线程池3.1 头文件3.2 线程池类3.3 构造函数实现3.4 入队—添…

ModStartBlog v6.4.0 升级输入过滤、多文件组件,修复已知问题

V6.4.0版本更新 2022年12月20日ModStartBlog发布v6.4.0版本&#xff0c;增加了以下14个特性&#xff1a; [新功能] 富文本过滤规则调整优化[新功能] ArrayPackage数组输入数据包处理器[新功能] 请求输入组件新增多文件路径类型[新功能] 多文件上传组件[新功能] 所有组件新增t…

RCE代码及命令执行(详解)

RCE代码及命令执行1.RCE漏洞1.1.漏洞原理1.2.漏洞产生条件1.3.漏洞挖掘1.4.漏洞分类1.4.1.命令执行1.4.1.1.漏洞原理1.4.1.2.命令执行危险函数1.4.1.3.漏洞检测1.4.2.代码执行1.4.2.1.漏洞原理1.4.2.2.代码执行危险函数1.4.2.3.漏洞检测1.5.命令执行和代码执行区别2.命令执行2.…

QT学习记录(二)最基础的工程

文件 工程新建后会有这几个文件&#xff0c;自动生成的 main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.show();return a.exec(); }QApplication a(argc, argv);这里…

Mentor-dft 学习笔记 day45-MTFI

Using MTFI Files此节介绍MTFI&#xff08;Mentor Tessend Fault Information&#xff09;功能&#xff0c;可用于ATPG工具和Tessent LogicBIST。MTFI是用于存储故障状态信息的通用且可扩展的文件格式。MTFI File Format MTFI是在“dft-edt”和“patterns-scan”上下文中读取和…

运用大O来给代码提速(冒泡排序)

本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余&#xff0c;我决定把这本书精彩的部分摘录出来与大家分享。 本章内容 写在前面 1.冒泡排序 2.冒泡排序实战 3.冒泡排序的实现 4.冒泡排序的效率 5.二次问题 6.线性解决 7.总结 写在前面 大 O记…

Diffusion Model合集 part2

扩散模型原理介绍2五&#xff0c;逆扩散过程(Reverse Process)六&#xff0c;扩散过程中的后验的条件概率q(xt−1∣xt,x0)q(x_{t-1}|x_{t},x_{0})q(xt−1​∣xt​,x0​)七&#xff0c;目标数据分布的似然函数八&#xff0c;Diffusion Probabilistic Model的算法代码五&#xff…

“专利费用减缓”怎么申请?

在专利申请时&#xff0c;很多申请人对“专利费用减缓”的概念并不了解&#xff0c;或者不太清楚。甚至有很多申请人一听到专利申请可以请求费用减缓&#xff0c;就以为申请专利是不要钱的。当然&#xff0c;这样的理解就存在了很大的偏差了&#xff0c;所以&#xff0c;我们今…

Java Arrays类

JavaArrays类\huge{Java \space Arrays类}Java Arrays类 概述 Arrays类Arrays类Arrays类&#xff1a;本质就是一个工具类&#xff0c;用于操作数组元素的。 常用API ①. toString()&#xff08;重写过的toString方法&#xff09; toString()toString()toString()&#xff1a…

人才招聘网 招聘系统源码找工作源代码 各行业的招聘网站门户源码

运行环境&#xff1a;VS2015SqlServer2008R2.NET4.0 系统介绍&#xff1a; 适用于各地方或者各行业的招聘网站门户&#xff0c;相当强大的人才招聘网站&#xff0c;可登陆注册、支持第三方登陆&#xff0c;强大额后台管理&#xff0c;功能全面&#xff0c;界面美观 职位和简历…

如何用yolov5 训练自己的数据

文章目录 说明数据准备划分数据集转换数据label 格式训练前准备修改图片路径修改训练配置文件 voc.yaml修改 yolo配置文件开始训练没有GPU 或显存不够的说明 利用yolov5 训练自己的数据集通常需要利用自己标注的数据进行训练 接下来记录下如何训练自己的数据 数据准备 我这用的…

【数据结构】- 面试题

面试题1. 删除链表中的节点2. 反转一个链表&#xff08;非递归解法&#xff09;3. 判断一个链表是否有环&#xff08;快慢指针&#xff09;问题&#xff1a;[快慢指针为什么一定会相遇](https://blog.csdn.net/Leslie5205912/article/details/89386769)4. 获取单链表的节点个数…

【轻量级开源ROS 的机器人设备(5)】--(3)拟议的框架——µROS节点

前文链接&#xff1a;(2条消息) 【轻量级开源ROS 的机器人设备&#xff08;5&#xff09;】--&#xff08;2&#xff09;拟议的框架——ROS节点 五、静态栈分析 在处理运行多个资源的严格受限平台时线程&#xff0c;重要的是将堆栈使用保持在最低限度。这甚至 在利用具有同质堆…

技嘉电脑怎么开启vt模式?

电脑开启vt模式后&#xff0c;可以提高主板的运行速率&#xff0c;提高性能。那就有一些使用技嘉电脑的用户问技嘉主板怎么开启vt模式&#xff1f;下面小编就来教教大家技嘉电脑开启vt模式的方法。 Intel芯片组的技嘉主板 1、一般情况下&#xff0c;也就是在电脑开机的时候&…

计算机网络、操作系统刷题笔记15

计算机网络、操作系统刷题笔记15 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sq…

差分详细讲解(C++)

每日一句:平凡的我在人多的地方曾极力小心翼翼&#xff0c; 但不知从何时起 &#xff0c;我不太在意别人的目光了。比起被人觉得是个怪人&#xff0c;我现在更害怕浪费时间。 差分一、一维差分二、二维差分一、一维差分 差分就是前缀和的逆运算,如果你不懂什么是前缀和,看这里…

移动技术在仓库运营管理中的作用

作者&#xff1a;Mike Kay&#xff0c;Mendix公司合作伙伴The Config Team渠道客户经理 市面上出现越来越多的仓库管理系统&#xff08;WMS&#xff09;以满足企业更好地管理供应链的需求。想要充分挖掘WMS的优点&#xff0c;一般可以通过移动解决方案来将关键的供应链运作进…