题意:经典的八数码问题,题目已确定好目标状态,若从起始状态到目标状态有解,输出最短的到达路径的每一步操作;否则输出unsolvable。
思路:这是一道经典的八数码问题,采用BFS+康托展开判重,主要多了一步打印路径,把可变化位置x看成数字0放置位置,共有9个数字,即9!种状态,在数组可开的范围内,用pre结构体记录当前状态的前驱状态即转化过来的方式,再用栈倒过来打印答案即可。(BFS时用STL中的queue会超时…故得手写队列,顺便手写了栈)
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=362880;//9!(把空位置x看成数字0)
char op[]={'u','d','l','r'};//上,下,左,右;
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//方向数组要与op[]对应,便于操作;
struct node
{
int state[9];
int step;
int id;
};
struct pre//存前驱节点的标号和从前驱节点到当前节点的操作下标;
{
int id;
int operation;
}pre[maxn+10];
bool vis[maxn+10];
int start[9],target[9];//起始状态,终止状态;
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//阶乘表(0~9);
node que[maxn*2];
int sta[maxn+10];
int front,rear;//队头,队尾指针;
int top;//栈顶指针;
bool Cantor(int str[],int n)//康托展开判重;
{
int result=0;
for(int i=0;i<=n;i++)
{
int count=0;
for(int j=i+1;j<=n;j++)
{
if(str[i]>str[j])
{
++count;
}
}
result+=(count*fac[n-i]);
}
if(!vis[result])
{
vis[result]=true;
return true;//表示还没走过;
}
else
{
return false;
}
}
void get_ans(node now)
{
int pos=now.id;
while(pre[pos].id!=-1)
{
sta[top++]=pre[pos].operation;
pos=pre[pos].id;
}
while(top!=0)
{
int now=sta[--top];
printf("%c",op[now]);
}
printf("\n");
}
void bfs()
{
node s;
memcpy(s.state,start,sizeof(s.state));
s.id=0;
pre[s.id].id=-1;
pre[s.id].operation=-1;
s.step=0;
Cantor(s.state,8);
que[rear++]=s;
int pos=0;//枚举当前是第几个状态,便于回溯打印路径;
while(front<rear)
{
node now=que[front++];
if(memcmp(now.state,target,sizeof(target))==0)//成功;
{
get_ans(now);
return ;
}
int i;
for(i=0;i<=8;i++)//找可转移状态的点0;
{
if(now.state[i]==0)
{
break;
}
}
//转化成3*3矩阵,左上角为(0,0);
int x=i/3;
int y=i%3;
for(int j=0;j<4;j++)
{
node in;
int dx=x+dir[j][0];
int dy=y+dir[j][1];
if(dx<0||dx>=3||dy<0||dy>=3) continue;
int where=dy+3*dx;//转化为一维;
memcpy(in.state,now.state,sizeof(in.state));
swap(in.state[where],in.state[i]);
in.step=now.step+1;
if(Cantor(in.state,8))//判重
{
in.id=(++pos);
pre[in.id].id=now.id;
pre[in.id].operation=j;
que[rear++]=in;
}
}
}
printf("unsolvable\n");
}
int main()
{
int i,j;
for(i=0;i<=8;i++)
{
char c[2];
scanf("%s",c);
if(c[0]>='0'&&c[0]<='9')
{
start[i]=c[0]-'0';
}
else
{
start[i]=0;
}
}
for(i=0;i<=8;i++)//构造终止状态;
{
if(i!=8)
{
target[i]=i+1;
}
else
{
target[i]=0;
}
}
memset(vis,false,sizeof(vis));
front=rear=0;
top=0;
bfs();
return 0;
}