原题
思路:本题题意比较清楚,要找到最小的操作数,我们可以将每次九宫格表示的不同字符串看做一种不同的状态,从一种状态变化到另一种状态,则操作数 +1,问你从起始状态变化到目标状态的最小操作数,那么本题可以看做是求一个各边权值相同的最短路问题,所以用BFS即可。因为我们要记录每种状态是否走过,可将这种状态的字符串hash为一个数字,而如果直接字符串hash无法控制数值大小,v[N]数组(记录访问数组)可能开很大,如果使用康拓展开(全排列hash),那么就能保证最大hash值为10!,且不会冲突。
全排列hash:一个字符串的hash值为从 0~i 位对 d[i] * (i!) 求和,其中 d[i] 表示第 i 位上逆序对的数量。使用这种方法不会浪费空间且绝不冲突,时间复杂度 O(n*n),n(字符串长度)最好不要超过12
代码:
//康拓展开全排列hash
#include<bits/stdc++.h>
using namespace std;
const int N = 5e6+10;
int v[N],p[10],dis[N];
int dir[4][2]={-1,0,1,0,0,1,0,-1};
int hashxx(string s)
{
int ans=0;
for(int i=0;i<9;i++)
{
int d=0;
for(int j=0;j<i;j++)
{
if(s[j]>s[i])
d++;
}
ans+=d*p[i];
}
return ans;
}
int bfs(string start)
{
dis[hashxx(start)]=0;
string ee="12345678x";
queue<string>q;
q.push(start);
while(!q.empty())
{
string z=q.front();
q.pop();
if(z==ee)
return dis[hashxx(z)];
int t=z.find('x');
int juli=dis[hashxx(z)];
int x=t/3,y=t%3;
for(int i=0;i<4;i++)
{
int a=x+dir[i][0];
int b=y+dir[i][1];
if(0<=a&&a<3&&0<=b&&b<3)
{
swap(z[a*3+b],z[t]);
if(!v[hashxx(z)])
{
v[hashxx(z)]=1;
dis[hashxx(z)]=juli+1;
q.push(z);
}
swap(z[a*3+b],z[t]);
}
}
}
return -1;
}
int main()
{
p[0]=1;
for(int i=1;i<10;i++)
{
p[i]=p[i-1]*i;//先预处理出阶乘数
}
string s,start;
for(int i=1;i<=9;i++)
{
cin>>s;
start+=s;
}
cout<<bfs(start);
return 0;
}