洛谷P1379八数码难题

题目描述

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

要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765)。

找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入输出格式

输入格式:

输入初始状态,一行九个数字,空格用0表示

输出格式:

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

输入输出样例

输入:

283104765

输出:

4

分析:

八数码难题,对于我这个C++蒟蒻来说还真有点难,在经过dalao的指点后终于AC了这道题.

先将思路,题面很短相信大家都能看懂,虽然我的代码较长但是应该是最好理解的吧,由题目得知我们有9个数,

其中有一个数字0剩下的数字都能和0交换位置即在数字0能和它的上下左右交换位置(在不超出边界的情况下),

然后我们需要知道当一个矩阵最后交换到"123804765"这种情况的最小步数,最小又是矩阵从这一点我们可以判断出,

此题很有可能用BFS来写,那么问题又来了我们怎么标记?转换成矩阵标记?不不不那样就太麻烦了而且代码也不好实现,

再看题"123804765"其实这已经提示我们可以用字符串来标记,那么怎么标记,我们可以用map<string,bool> vis来实现这个标记功能,

用map就能存下字符型的数组了所以我们每次仅需要判断vis[XX]是否走过就可以完成标记了.

接下来将搜索的实现.

标记我们知道怎么实现,那么关于这道题的搜索又怎么实现?

我们可以由题目得知每个数字只能和0进行交换,我们就可以想到我们可以用0进行对矩阵中每个数字进行更新我们可以用swap实现,

对两个数字进行交换,因为能进行数字交换只能是单个字符的操作所以我们就又得将这些字符全部压进一个新的字符串里这里使用

字符串.push_back(字符)即可实现,然后将新的字符串进行判断是否和"123804765"相同如果相同我们就输出当前的步数,由BFS的最优性

如果满足当前的情况的那就是最优解,注意这里一定要想清楚因为我们每次交换一次每次都更新整个字符串所以当字符串重复时这个情况是不满足的

所我们还要将字符swap回去,当然因为每次交换都会使Map的值发生改变,所以我们也必须swap回去,知道了这些这道题写起来就比较容易了.

在搜索开始前我们当然要进行对这个字符串的预处理当然这个要实现很简单我在这里就不不解释了

更详细的理解请看代码

代码:

 

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <map>
using namespace std;
int startx,starty,posx=1,posy=1;//我这个变量名这么清楚可能没人不懂对吧
string s;
char Map[10][10];
int dirx[4]={1,0,0,-1};//因为只有四个方向
int diry[4]={0,1,-1,0};
map<string,bool> vis;//这里我们用map来进行判断重复
struct Node //结构体来存数
{
    int x,y,t;
    string s;//这里我们声明s来进行判断和更新
};
void bfs()
{
    vis[s]=true;//起点肯定要赋值为true啊
    queue<struct Node> que;
    struct Node now;
    now.x=startx;now.y=starty;now.t=0;now.s=s;
    que.push(now);
    while(!que.empty())
    {
        now=que.front();
        que.pop();
        posx=1,posy=1;
        for(int i=0;i<s.length();i++)//其实这里写在下面比较好理解但不影响
        {
            Map[posx][posy]=now.s[i];//这里的操作就是我们将我们每次更新完的字符串再用MAP重新赋值再就进行搜索
            posy++;
            if(posy==4)//如果这一排满了
            {
                posy=1;//换下一排
                posx++;
            }
        }
        if(now.s=="123804765")//如果满足情况
        {
            cout<<now.t<<endl;//输出因为BFS最优的特点我们就可以直接输出
            return;
        }
        for(int i=0;i<4;i++)
        {
            int xx=dirx[i]+now.x;
            int yy=diry[i]+now.y;
            if(xx<1||xx>3||yy<1||yy>3) continue;//边界处理
            swap(Map[now.x][now.y],Map[xx][yy]);
            string change;//这里我们需要重新声明一个字符串来进行判断和更新操作
            for(int i=1;i<=3;i++)//因为3*3的矩阵
            {
                for(int j=1;j<=3;j++)
                change.push_back(Map[i][j]);//将字符全部压进字符串里
            }
            if(vis[change])//这里需要注意这里很重要当它为重复的情况我们还要swap回去才能continue
            {//因为如果我们当前这个不满足情况的值会改变会对后面的搜索产生干扰所以必须swap回去才能continue
                swap(Map[xx][yy],Map[now.x][now.y]);
                continue;
            }
            vis[change]=true;
            swap(Map[xx][yy],Map[now.x][now.y]);//这里和上面那个是差不多的意思这里因为我们这个BFS和以往的不同因为我们直接是将答案进行了跟新,所以如果我们不更新为上一个状态那会对下一个循环产生影响这个地方难理解需要好好想一下
            struct Node next;
            next.x=xx;next.y=yy;next.s=change;next.t=now.t+1;//这里BFS的基本操作
            que.push(next);
        }
    }
    return;
}
int main()
{
    cin>>s;
    for(int i=0;i<s.length();i++)//预处理
    {
        Map[posx][posy]=s[i];
        if(Map[posx][posy]=='0')//0为起点
        {
            startx=posx;//找到它的坐标
            starty=posy; 
        }
        posy++;
        if(posy==4)//当这排y为4时这排就满了因为我是从1开始的所以要到4
        {
            posy=1;//posy赋值为1
            posx++;//进入到下一排
        }
    }
    bfs();
    return 0;
}

 

转载于:https://www.cnblogs.com/CCCPKeay/p/9897182.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这道题目要求模拟一个棋盘棋子进行移动的过程。棋盘为3×3大小,每个棋子有一个数字标记,数字范围在1至8之间。棋盘中留有一个空格,用数字0表示。要求解的问题是:给出一种初始布局(即初始状态)和目标布局(即目标状态),找到一种最少步骤的移动方法,将布局从初始布局转换为目标布局。 求解的方法:给出一种初始布局和目标布局,可以采用广度优先搜索(BFS)的方法进行求解。具体步骤为:从初始状态开始,遍历所有可能的棋盘状态,并记录每个状态的深度(即移动的步数)。当遍历到目标状态时,返回最少步数即为答案。 具体实现过程中,可以先将初始状态和目标状态都转换为一维数组形式。在搜索过程中,每次移动棋子时,维护一个标记每个棋子位置的数组,并依次枚举每个棋子可以移动的方向,例如向上、向下、向左或向右。移动过程中,注意将移动前后的状态保存下来,并判断移动后的状态是否合法。若合法,则将新状态加入队列中,继续搜索下一级状态。 当搜索到目标状态时,输出最小步数即可。 ### 回答2: 这道题目是经典的八数码问题,可以使用广度优先搜索算法求解。具体步骤如下: 1. 定义状态表示:将每个棋子的位置用一个九维数组表示,0表示空格。 2. 定义状态转移函数:枚举空格可以移动的方向(上下左右),并根据移动后的状态生成下一层状态。 3. 运用广度优先搜索算法:将初始状态加入队列,按层遍历状态空间,直到找到目标状态或者所有状态均已遍历。 4. 输出最少步骤的移动方法:通过记录每个状态的父状态,可以递归输出从初始状态到目标状态的所有移动方法。 总的来说,八数码问题是一道比较经典的搜索问题,并且也可以用深度优先搜索和A*算法等其他搜索算法求解。无论使用什么算法,都需要较大的时间和空间开销来维护状态空间,因此在实际应用中需要根据具体问题的特点选择合适的算法来求解。 ### 回答3: 这是一个经典的八数码问题,可以用广度优先搜索算法来解决。具体步骤如下: 1. 将初始状态和目标状态转化为一维数组形式,方便计算和比较。 2. 将初始状态压入队列中,并记录其步数为0。 3. 从队列中取出第一个元素,检查是否与目标状态相同,如果相同则输出步骤并结束,否则继续搜索。 4. 若当前状态与目标状态不同,则将当前状态的8个可行移动方向遍历一遍,每次将空格移动到一个新位置,生成新的状态,并将新状态压入队列中。 5. 在新状态中记录步数为当前状态步数+1,并将新状态继续遍历8个方向,直到搜索到目标状态。 6. 若队列为空而仍未找到目标状态,则无解。 7. 输出最短步数,以及转变过程中的每一个状态,即可完成八数码问题的求解。 需要注意的是,在搜索时需要判重,避免重复搜索已经搜索过的状态。另外,为了减少搜索时间和空间复杂度,可以使用双向广度优先搜索算法,即同时从初始状态和目标状态两个方向开始搜索,直到两个搜索队列中出现相同状态,即找到了最短路径。这种方法可以大大缩短搜索时间,并很好地避免了搜索复杂度过高的问题。 以上就是八数码问题的求解方法。虽然困难,但是只要遵循以上步骤,耐心地进行搜索,最终一定能够得出正确答案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值