1571.八数码
时限:5000ms 内存限制:20000K 总时限:10000ms
描述
在九宫格里放在1到8共8个数字还有一个是空格,与空格相邻的数字可以移动到空格的位置,问给定的状态最少需要几步能到达目标状态(用0表示空格):
1 2 3
4 5 6
7 8 0
输入
输入一个给定的状态。
输出
输出到达目标状态的最小步数。不能到达时输出-1。
#include <iostream>
#include <queue>
#include <map>
using namespace std;
struct node
{
int a[3][3]; //存储八数码
int zx,zy; //0的位置
int num; //将每个八数码状态转换成九位整数(最大为9876543210,int范围足够)
};
node target; //存储目标状态
queue <node> q1; //广搜所用队列
map <int, int> used; //判重
map <int, int> step; //存储步数
int bfs(); //广搜
node setnum(node n1); //计算n1.num
bool canmoveto(node n1, int d); //判断越界或重复
node moveto(node n1, int d); //返回新状态
bool totarget(node n1); //判断是否到达目标状态
int main()
{
node start; //输入初始状态
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
cin>>start.a[i][j];
if(start.a[i][j]==0) //记录0的位置
{
start.zx=i;
start.zy=j;
}
}
}
start=setnum(start); //计算start.num
//初始节点入队,并更新used和step数组
q1.push(start);
//cout<<used[start.num]<<endl;
used[start.num]=1;
step[start.num]=0;
//设定目标状态
int k=1;
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
target.a[i][j]=k;
k++;
}
}
target.a[2][2]=0;
//进行广搜
cout<<bfs()<<endl;
return 0;
}
int bfs()
{
node top,next;
while(!q1.empty())
{
top=q1.front();
//cout<<"gettop:"<<top.num<<endl;
q1.pop();
for(int d=0; d<4; d++) //将0与四个方向上的数字交换,0==左,1==下,2==右,3==上
{
if(canmoveto(top, d)) //判断重复和越界
{
next=moveto(top, d); //获得新状态
if(totarget(next)) //到达目标状态则返回
{
return step[next.num];
}
else //没到达目标状态则入队
{
q1.push(next);
/*
cout<<step[next.num]<<endl;
cout<<"push"<<next.num<<endl;
*/
}
}
}
}
return -1;
}
//计算n1.num
node setnum(node n1)
{
n1.num=0;
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
n1.num*=10; //将九个格内数字组合成九位整数
n1.num+=n1.a[i][j];
}
}
return n1;
}
//返回false条件:越界、重复
bool canmoveto(node n1, int d)
{
node n2; //先建立一个新节点,复制n1的状态
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
n2.a[i][j]=n1.a[i][j];
}
}
n2.zx=n1.zx;
n2.zy=n1.zy;
n2.num=n1.num;
switch(d)
{
case 0: //判断能否将0与左边数字交换
{
if(n2.zy==0) //越界
return false;
else
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx][n2.zy-1]); //将0与左边数字交换
n2.zy--; //更新0的位置
break;
}
}
case 1: //下
{
if(n2.zx==2)
return false;
else
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx+1][n2.zy]);
n2.zx++;
break;
}
}
case 2: //右
{
if(n2.zy==2)
return false;
else
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx][n2.zy+1]);
n2.zy++;
break;
}
}
case 3: //上
{
if(n2.zx==0)
return false;
else
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx-1][n2.zy]);
n2.zx--;
break;
}
}
}
n2=setnum(n2);
/*
cout<<d<<' '<<n2.num<<endl;
cout<<used.count(n2.num)<<endl;
*/
if(used.count(n2.num)!=0) //判重,重复返回false
{
//cout<<"chongfu"<<endl;
return false;
}
else
{
return true;
}
}
//返回新状态
node moveto(node n1, int d)
{
node n2;
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
n2.a[i][j]=n1.a[i][j];
}
}
n2.zx=n1.zx;
n2.zy=n1.zy;
n2.num=n1.num;
switch(d)
{
case 0: //将0与左边数字交换
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx][n2.zy-1]);
n2.zy--;
break;
}
case 1: //下
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx+1][n2.zy]);
n2.zx++;
break;
}
case 2: //右
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx][n2.zy+1]);
n2.zy++;
break;
}
case 3: //上
{
swap(n2.a[n2.zx][n2.zy],n2.a[n2.zx-1][n2.zy]);
n2.zx--;
break;
}
}
n2=setnum(n2);
//更新used数组,n2状态已用过
used[n2.num]=1;
//更新step数组,到达n2状态步数=1+到达n1状态步数
step[n2.num]=step[n1.num]+1;
//返回新状态n2
return n2;
}
//判断是否到达目标状态
bool totarget(node n1)
{
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
if(n1.a[i][j]!=target.a[i][j])
{
return false;
}
}
}
return true;
}
【后记】
1.写的时候出了个bug,把0和相邻数字交换位置后忘了更新0的新位置。
2.关于map判重,在这篇文章里有讲到https://blog.csdn.net/qq_41727666/article/details/83004756
3.每个八数码状态的编码方法如下图所示,判重时要用到:
扫描二维码关注公众号,回复:
3563490 查看本文章