题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用0表示
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入 283104765
输出
4
思路:使用bfs+set判重。
用结构体保存初末状态步数,并且入队,向四个方向寻找下一个状态放到set集合(判重)和队列(bfs)中,一直找下去,直到找到末状态.(使用时间:7.72s)
#include<bits/stdc++.h>
using namespace std;
typedef struct node {
char origin[10];
int step;
}node;
char last[10];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
queue<node>p;
set<string>vis;
void bfs()
{
while(!p.empty())
{
node head=p.front();
p.pop();
if(!strcmp(head.origin,last))
{
printf("%d\n",head.step);
return;
}
int pos;
for(int i=0;head.origin[i]!='\0';i++)
if(head.origin[i]=='0')
{
pos=i;
break;
}
int x1=pos/3;
int y1=pos%3;
for(int i=0;i<4;i++)
{
int x2=x1+dir[i][0];
int y2=y1+dir[i][1];
if(x2>=0&&x2<3&&y2>=0&&y2<3)
{
node t=head;
int pos1=x2*3+y2;
swap(t.origin[pos],t.origin[pos1]);
if(!vis.count(t.origin))
{
vis.insert(t.origin);
t.step++;
p.push(t);
}
}
}
}
printf("-1\n");
}
int main()
{
node t;
sscanf("123804765","%s",last);
scanf("%s",t.origin);
t.step=0;
p.push(t);
vis.insert(t.origin);
bfs();
return 0;
}
方法2:使用双向bfs+map判重(时间:221ms)比上一个快34倍
思路:一开始将出状态和末状态入队,出状态和末状态都向下一层延伸,当到达重合的节点时,终止条件是(步数和为3),只有末状态标记的是3,其他状态都是1。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
map<string,int>vis;
map<string,int>step;
string origin;
string last="123804765";
void bfs()
{
if(origin==last)
{
printf("0\n");
return;
}
queue<string>p;
p.push(origin);
p.push(last);//没将last插到队列中超限33% (双向bfs)
while(!p.empty())
{
string head=p.front();
p.pop();
int pos;
for(int i=0;i<head.length();i++)
if(head[i]=='0')
{
pos=i;
break;
}
int x=pos/3;
int y=pos%3;
for(int i=0;i<4;i++)
{
int x2=x+dir[i][0];
int y2=y+dir[i][1];
if(x2>=0&&x2<3&&y2>=0&&y2<3)
{
int pos1=x2*3+y2;
string t=head;
swap(t[pos],t[pos1]);
if(!vis.count(t))
{
vis[t]=vis[head];
step[t]=step[head]+1;
p.push(t);
}
else if(vis[head]+vis[t]==3)
{
printf("%d\n",step[head]+step[t]+1);
return ;
}
}
}
}
printf("-1\n");
}
int main()
{
cin>>origin;
vis[origin]=1;//标记除终点以外状态都是1
vis[last]=2;//终点状态2
step[origin]=0;//步数
bfs();
return 0;
}
总结:紫书P199讲的就是八数码问题,这道题的方法多种,以上仅为两种方法,第二种方法相对是最优解。也看到过0ms的最优解,膜拜~~~(c语言网的九宫重排跟这道题类似)