SSL-ZYC 洛谷P1379 八数码难题

题目大意:
在3×3的棋盘上,摆有八个棋子,每一个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子能够移到空格中。要求解的问题是:给出一种初始布局和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。ios


思路:web

BFS+HASH

用bfs求出正确答案,hash判重。
真不知道为何洛谷评为 提升+/省选- 难度svg


代码:布局

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

const int dx[5]={0,0,0,-1,1};
const int dy[5]={0,1,-1,0,0};
const int k=362881;
int x,y,ok,sum,head,tail,q;
int state[k][4][4],father[k],a[4][4],ha[k];

int locate(int x)  //哈希判重
{
    int y=0;
    for (int i=1;i<=3;i++)
     for (int j=1;j<=3;j++)
      y=y*10+state[x][i][j];
    int t=y%k;
    int i=0;
    while (i<k&&ha[(t+i)%k]!=0&&ha[(t+i)%k]!=y) i++;
    if (ha[(t+i)%k]==0)
    {
        ha[(t+i)%k]=y;
        return 0;
    }
    else return 1;
}

void print(int x)  //输出
{
    if (father[x]==0) return;
    print(father[x]);
    sum++;
    return;
}

void bfs()  //广搜
{
    father[1]=0;
    head=0;
    tail=1;
    do
    {
        head++;
        for (int i=1;i<=4;i++)
        {
            for (int j=1;j<=3;j++)
             for (int k=1;k<=3;k++)
             {
                if (state[head][j][k]==0)
                {
                    x=j;
                    y=k;
                    break;
                }
             } 
            if (x+dx[i]<1||x+dx[i]>3||y+dy[i]<1||y+dy[i]>3) 
                                               continue;  //出界
            tail++;
            father[tail]=head;
            for (int j=1;j<=3;j++)
             for (int k=1;k<=3;k++)
              state[tail][j][k]=state[head][j][k];
            state[tail][x][y]=state[tail][x+dx[i]][y+dy[i]];
            state[tail][x+dx[i]][y+dy[i]]=0;  //交换(移动)
            if (locate(tail)==1) tail--;  //判重
            ok=1;
            for (int j=1;j<=3;j++)
             for (int k=1;k<=3;k++)
              if (state[tail][j][k]!=a[j][k])
              {
                  ok=0;
                  break;
              }
            if (ok==1)  //达到目标
            {
                print(tail);  //输出
                tail=-1;
                return;
            }
        }
    }    
    while(head<tail);
}

int main()
{
    a[1][1]=1;a[1][2]=2;a[1][3]=3;
    a[2][1]=8;a[2][2]=0;a[2][3]=4;
    a[3][1]=7;a[3][2]=6;a[3][3]=5;  //初始化
    for (int i=1;i<=3;i++)
     for (int j=1;j<=3;j++)
      scanf("%1d",&state[1][i][j]);
    for (int i=1;i<=3;i++)
     for (int j=1;j<=3;j++)
     {   
         q++;
         if (q==9) q=0;
         if (state[1][i][j]!=q) break;
         if (q==0)   //特判
         {
             puts("0");
             return 0;
         }
     } 
     locate(1);   //保存初始状况
     bfs();
     printf("%d\n",sum);
     return 0;
}