ACWing845 八数码(BFS,全排列hash)

原题
思路:本题题意比较清楚,要找到最小的操作数,我们可以将每次九宫格表示的不同字符串看做一种不同的状态,从一种状态变化到另一种状态,则操作数 +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;
}

戒骄戒躁,百炼成钢!
原文地址:https://www.cnblogs.com/Pecoz/p/13732635.html