我们先来看一下题目(可以去洛谷网站读题,如果题目已经读过了可以跳过)
题目
题目描述
在 3×3 的棋盘上,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格,空格用 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用 0 表示。
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数。保证测试数据中无特殊无法到达目标状态数据。
输入输出样例
输入 #1
283104765
输出 #1
4
说明/提示
样例解释
图中标有 0 的是空格。绿色格子是空格所在位置,橙色格子是下一步可以移动到空格的位置。如图所示,用四步可以达到目标状态。
并且可以证明,不存在更优的策略。
题目分析
别看题目是八数码难题,实际上一点都不难(自己光查错就查了很长时间)
这题要求我们找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变,所以这道题最好用宽搜而不是用深搜
讲解
变量定义
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};//从当前的位置到上下左右,横坐标和纵坐标要变的数值
string s1,s2;//s1为初始布局,s2为目标布局,即“123804765”
struct node//定义结构体
{
string s;//布局(状态)
int step;//到这个布局用了多少步
};//注意这里要加“;”号
queue<node>q;//定义结构体类型的队列
map<string,int> ma;//映射
每个变量的含义见代码里的批注
queue是队列,遵循“先进先出”的原则
map是映射,作用是标记这个状态有没有被搜过
注意:定义结构体类型的后面的那个大括号,后面别忘了加“;”号!!!(本蒟蒻就是在这里查了半天错)
1.主函数main
我们只需要输入初始布局,把"123804765"这个目标状态赋值给s2,再进入bfs就可以了
代码:
int main()
{
int i,j,k;
cin>>s1;
s2="123804765";
bfs();
return 0;
}
2.bfs(宽搜)
找到“0”(空格)的位置,然后将它与上下左右四个方向上的数进行交换,查找可能的情况,如果查找到某种情况是目标状态,因为是宽搜,所以保证查找到目标状态时一定是最少步骤,所以可以直接输出,跳出bfs,无需继续查找
代码:
void bfs()//宽搜
{
q.push({s1,0});
ma[s1]=1;//将初始状态进队,并标记这个状态已经被查找过了,避免重复查找
while(q.size())
{
node t=q.front();//得到上一个状态
q.pop();//弹出队头
int w=t.s.find('0');//找到"0"在字符串的位置,即空格的位置
int x=w/3,y=w%3;//x,y表示“0”的横坐标和纵坐标
if(t.s==s2)//如果已经达到目标状态,则直接输出最少步骤,跳出bfs
{
printf("%d",t.step);//输出最少步骤
return;//跳出bfs
}
for(int i=1;i<=4;i++)
{
int a=x+dx[i],b=y+dy[i];//a,b表示“0”的上下左右四个位置的横坐标和纵坐标
if(a>=0&&a<=2&&b>=0&&b<=2)//判断是否越界
{
swap(t.s[a*3+b],t.s[w]);//交换“0”与被交换的位置上的数
if(ma[t.s]==0)//如果这个状态还没有被搜过,则把这个状态进队
{
ma[t.s]=1;//标记这个状态为1,即已经被查找过了
q.push({t.s,t.step+1});//把这个状态进队
}
swap(t.s[a*3+b],t.s[w]);//别忘了再交换回来,回到上一步,以便再查找下一种可能
}
}
}
}
完整AC代码
#include <bits/stdc++.h>
using namespace std;
int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1};//从当前的位置到上下左右,横坐标和纵坐标要变的数值
string s1,s2;//s1为初始布局,s2为目标布局,即“123804765”
struct node//定义结构体
{
string s;//布局(状态)
int step;//到这个布局用了多少步
};//注意这里要加“;”号
queue<node>q;//定义结构体类型的队列
map<string,int> ma;//映射
void bfs()//宽搜
{
q.push({s1,0});
ma[s1]=1;//将初始状态进队,并标记这个状态已经被查找过了,避免重复查找
while(q.size())
{
node t=q.front();//得到上一个状态
q.pop();//弹出队头
int w=t.s.find('0');//找到"0"在字符串的位置,即空格的位置
int x=w/3,y=w%3;//x,y表示“0”的横坐标和纵坐标
if(t.s==s2)//如果已经达到目标状态,则直接输出最少步骤,跳出bfs
{
printf("%d",t.step);//输出最少步骤
return;//跳出bfs
}
for(int i=1;i<=4;i++)
{
int a=x+dx[i],b=y+dy[i];//a,b表示“0”的上下左右四个位置的横坐标和纵坐标
if(a>=0&&a<=2&&b>=0&&b<=2)//判断是否越界
{
swap(t.s[a*3+b],t.s[w]);//交换“0”与被交换的位置上的数
if(ma[t.s]==0)//如果这个状态还没有被搜过,则把这个状态进队
{
ma[t.s]=1;//标记这个状态为1,即已经被查找过了
q.push({t.s,t.step+1});//把这个状态进队
}
swap(t.s[a*3+b],t.s[w]);//别忘了再交换回来,回到上一步,以便再查找下一种可能
}
}
}
}
int main()
{
int i,j,k;
cin>>s1;
s2="123804765";
bfs();
return 0;
}
后言
如果本篇题解有漏洞,欢迎大家来批斗本蒟蒻