历届试题 九宫重排
时间限制:1.0s 内存限制:256.0MB
问题描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
123.46758
样例输出
3
样例输入
13524678.
46758123.
46758123.
样例输出
22
本题是A*算法的典型例题,练习之后可以对A*算法的原理有一定的理解。但是本文主要讲解另一种方法(A*算法抽空发布)。
本题主要的难点在于状态的表达,数学中有种方式叫康托展开,是一种可以解决一些序列问题的算法。例如可以把一个序列变为一个数字,排列3 5 7 4 1 2 9 6 8展开为98884,因为X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884。同理,也可以把数字转化为序列,叫做逆康托展开。(详情百度)
明白康托展开和逆康托展开后,就可以通过bfs求最少步数了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=500000;//康托展开的最大数字
struct Node
{
int val;
int ans;
};
int fac[11]={1,1,2,6,24,120,720,5040,40320,362880};//0~8的阶乘
int st,goal;
bool vis[maxn];//标记康托展开的数字
int SetKT(char *str)//康托展开成数字
{
int ans=0;
bool use[10];
memset(use,0,sizeof(use));
for(int i=0;i<9;i++)
{
int val=str[i]-'0',cnt=0;
for(int j=1;j<val;j++)
if(!use[j])
cnt++;
ans+=cnt*fac[8-i];
use[val]=1;
}
return ans+1;
}
void GetKT(int val,char *str)//逆康拓展开,用数组str[]保存序列
{
val--;
bool use[10];
memset(use,0,sizeof(use));
for(int i=0;i<9;i++)
{
int num=val/fac[8-i],cnt=0;
val=val%fac[8-i];//余数
for(int j=1;j<=9;j++)
if(!use[j])
if(++cnt==num+1)
{
str[i]=j+'0';
use[j]=1;
break;
}
}
str[9]='\0';
}
void Deal(char *str)
{
for(int i=0;i<strlen(str);i++)
if(str[i]=='.')
str[i]='9';
}
int bfs()
{
queue<Node> q;
memset(vis,0,sizeof(vis));
Node node;
node.val=st;
node.ans=0;
q.push(node);//进队列
vis[st]=1;//标记访问
while(!q.empty())
{
Node p=q.front();
q.pop();
char str[11];
GetKT(p.val,str);//解码
int index;
for(int i=0;i<strlen(str);i++)
if(str[i]=='9')//寻找空格位置
{
index=i;
break;
}
//根据空格位置进行操作
if(index%3>0)
{
swap(str[index],str[index-1]);
int val=SetKT(str);
if(val==goal)
return p.ans+1;
if(!vis[val])
{
node.val=val;
node.ans=p.ans+1;
q.push(node);
vis[val]=1;
}
swap(str[index],str[index-1]);//恢复
}
if(index%3<2)
{
swap(str[index],str[index+1]);
int val=SetKT(str);
if(val==goal)
return p.ans+1;
if(!vis[val])
{
node.val=val;
node.ans=p.ans+1;
q.push(node);
vis[val]=1;
}
swap(str[index],str[index+1]);//恢复
}
if(index/3>0)
{
swap(str[index],str[index-3]);
int val=SetKT(str);
if(val==goal)
return p.ans+1;
if(!vis[val])
{
node.val=val;
node.ans=p.ans+1;
q.push(node);
vis[val]=1;
}
swap(str[index],str[index-3]);//恢复
}
if(index/3<2)
{
swap(str[index],str[index+3]);
int val=SetKT(str);
if(val==goal)
return p.ans+1;
if(!vis[val])
{
node.val=val;
node.ans=p.ans+1;
q.push(node);
vis[val]=1;
}
swap(str[index],str[index+3]);//恢复
}
}
return -1;
}
int main()
{
char str[11],end[11];
scanf("%s",str);
scanf("%s",end);
Deal(str);
Deal(end);
st=SetKT(str);
goal=SetKT(end);
printf("%d\n",bfs());
return 0;
}