真·题·再·现
【题目描述】
在3
×3
的棋盘上,摆有八个棋子,每个棋子上标有1
至8
的某一数字。棋盘中留有一个空格,空格用0
来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局,为了使题目简单, 设目标状态为
1 2 3
8 0 4
7 6 5
找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
【输入描述】
三行,每行三个整数,表示方阵的开始状态
【输出描述】
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数,若在5000
步内无解,则输出-1
【输入样例】
1 2 3
8 4 0
7 6 5
【输出样例】
1
解·题·思·路
【题目分析】
这道题目有十分甚至九分的复杂,需要同时涉及矩阵、宽度优先搜索、康托展开和康托展开逆运算四个知识点,所以本期题解可能会比较长。
首先,为了方便声明visited
数组,我们需要将矩阵转为整型变量。
这时有一个问题:矩阵转化的整型变量最大值事876543210
,也就是说布尔型visited
数组会占用876543210
字节的空间,而题目要求的字节只有134217728
字节,所以存储空间不够用力!(大悲)
但在仔细看看:从786543210
到801234567
之间有14691356
个数,这些数都没有对应的情况,白白浪费了14691356
个字节,所以说刚才说的这种方法浪费了很多空间。这时,就需要用到康托展开节省这些空间。
空间省出来之后,还需要用康托展开逆运算转换回矩阵形态。
【解题过程】
由于本期题目过于复杂,直接上代码,大家可以自己分析一下。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=370000;
int fac[15]={1,1,2,6,24,120,720,5040,40320,362880};//记录1-10的阶乘
bool vis[MAXN];
int step[MAXN];
int q1[MAXN],q2[MAXN],front,rear;//q1放数字值,q2放康托压缩后的值
int Cantor(int a[],int n){
int s=1;
int sum=1;
for(int i=0;i<n;i++){
s=0;
for(int j=i+1;j<n;j++)if(a[j]<a[i])s++;
sum+=s*fac[n-1-i];
}
return sum;
}
int getnum(int a[]){
int ans=0;
for(int i=0;i<9;i++) {
ans*=10;
ans+=a[i];
}
return ans;
}
int main(){
memset(step,-1,sizeof(step));
int a[9]={1,2,3,8,0,4,7,6,5};
int end=getnum(a);
int endCantor=Cantor(a,9);
for(int i=0;i<9;i++)cin>>a[i];
int begin=getnum(a);
int beginCantor=Cantor(a,9);
vis[beginCantor]=1;
step[beginCantor]=0;
q1[rear]=begin;
q2[rear]=beginCantor;
rear++;
if(begin==end){
cout<<0<<endl;
return 0;
}
while(front<rear){
int temp=q1[front];
int tempCantor=q2[front];
front++;
int find;
int t=temp;
for(int i=8; i>=0; i--){
a[i]=t%10;
t/=10;
if(a[i]==0)find=i;
}
if(find>=3){
swap(a[find],a[find-3]);
t=Cantor(a,9);
if(!vis[t]){
vis[t]=1;
step[t]=step[tempCantor]+1;
q2[rear]=t;
t=getnum(a);
if(t==end)break;
q1[rear]=t;
rear++;
}
swap(a[find],a[find-3]);
}
if((find+1)%3!=0){
swap(a[find],a[find+1]);
t=Cantor(a,9);
if(!vis[t]){
vis[t]=1;
step[t]=step[tempCantor]+1;
q2[rear]=t;
t=getnum(a);
if(t==end)break;
q1[rear]=t;
rear++;
}
swap(a[find],a[find+1]);
}
if(find<=5){
swap(a[find],a[find+3]);
t=Cantor(a,9);
if(!vis[t]){
vis[t]=1;
step[t]=step[tempCantor]+1;
q2[rear]=t;
t=getnum(a);
if(t==end)break;
q1[rear]=t;
rear++;
}
swap(a[find],a[find+3]);
}
if(find%3!=0){
swap(a[find],a[find-1]);
t=Cantor(a,9);
if(!vis[t]){
vis[t]=1;
step[t]=step[tempCantor]+1;
q2[rear]=t;
t=getnum(a);
if(t==end)break;
q1[rear]=t;
rear++;
}
swap(a[find],a[find-1]);
}
}
if(step[endCantor]==-1)cout<<-1;
else cout<<step[endCantor];
return 0;
}
【测试结果】
全部样例均正确,压力马斯内!!!