题目解析——八数码

八数码是一道经典的搜索题,解法比较多,效率也不一样,今天我们来讲两种效率较高的解法——IDA*和双向BFS


 

题目来源:洛谷P1379

题目描述

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

输入输出格式

输入格式:

输入初始状态,一行九个数字,空格用0表示

输出格式:

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数


解法一:IDA*

对于这个问题,我们首先可以想到的就是直接DFS或BFS,根据实际测试,洛谷的数据可以用BFS水过,但是时间大都在8000ms左右

我们来考虑如何优化,发现引入一个估价函数h可以达到更好的效率,并且我们不难想到,用当前数字到正确位置的曼哈顿距离之和当做估价函数h,公式如下:

           

于是我们限定一个深度deep,如果当前的step + h > deep,我们就认为这是不合法的方案

通过这个优化,我们就可以提高搜索效率了(实际测试中时间在2000ms左右)

代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;

string a;
int stx,sty;
int st[4][4];
int deep;
int ans[10][2] = {{2,2},{1,1},{1,2},{1,3},{2,3},{3,3},{3,2},{3,1},{2,1}};
int judge;

inline void init()
{
  int k = 0;
  for(int i = 1;i < 4;i ++)
    for(int j = 1;j < 4;j ++)
    {
      st[i][j] = a[k ++] - '0';
      if(st[i][j] == 0){ stx = i; sty = j; }
    }
  return ; 
}

inline int target()
{
  int exp = 0;
  for(int i = 1;i < 4;i ++)
  {
    for(int j = 1;j < 4;j ++)
    {
        if(!st[i][j]) continue;
        else exp += abs(i - ans[st[i][j]][0]) + abs(j - ans[st[i][j]][1]);
    }
  }
  return exp;
}

int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

inline void Astar(int nx,int ny,int step)
{
  int h = target();
  if(step + h > deep) return ;
  if(h == 0){ judge = 1; return ; }
  for(int pro = 0;pro < 4;pro ++)
  {
    int fx = nx + dx[pro];
    int fy = ny + dy[pro];
    if(fx < 1||fx > 3||fy < 1||fy > 3) continue;
    swap(st[nx][ny],st[fx][fy]);
    Astar(fx,fy,step + 1);
    swap(st[nx][ny],st[fx][fy]);
  }
}

int main(int argc, char const *argv[]) 
{
  cin>>a;
  init();
  if(!target()){ printf("0"); return 0; }
  while(++ deep)
  {
    Astar(stx,sty,0);
    if(judge){ printf("%d",deep); break; }
  }
  return 0;
}

解法二:双向BFS

我们可以发现,题目已经将末状态给我们了,所以,我们可以使用双向BFS,从两端向中间搜索,我们可以使用hash等一系列技巧来优化算法,在这里我使用的是将矩阵转化为数列

在实际测试中,其效率也是非常的优秀(200ms~400ms)

代码如下:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#define ll long long

using namespace std;

const int ans = 123804765;
int n;
queue <int> q;
int sqr[10][10];
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,1,-1};
map <int,int> vis;
map <int,int> step;

inline void bfs()
{
  if(n == ans)
  {
    printf("0");
    return ;
  }
  q.push(n);
  q.push(ans);
  vis[n] = 1;
  vis[ans] = 2;
  step[n] = 0;
  step[ans] = 1;
  while(!q.empty())
  {
    ll state = q.front();
    q.pop();
    ll now = state;
    ll nx,ny;
    for(int i = 3;i ;i --)
    {
      for(int j = 3;j ;j --)
      {
        sqr[i][j] = now % 10;
        now /= 10;
        if(sqr[i][j] == 0)
        {
          nx = i;
          ny = j;
        }
      }
    }
    for(int i = 0;i < 4;i ++)
    {
      ll fx = nx + dx[i];
      ll fy = ny + dy[i];
      if(fx > 3||fx < 1||fy > 3||fy < 1) continue;
      swap(sqr[fx][fy],sqr[nx][ny]);
      ll New = 0;
      for(int i = 1;i <= 3;i ++)
        for(int j = 1;j <= 3;j ++)
          New = New * 10 + sqr[i][j];
      if(vis[New] == vis[state]) 
      {
        swap(sqr[fx][fy],sqr[nx][ny]);
        continue;
      }
      if(vis[New] + vis[state] == 3)
      {
        printf("%d",step[New] + step[state]);
        return ;
      }
      vis[New] = vis[state];
      step[New] = step[state] + 1;
      q.push(New);
      swap(sqr[fx][fy],sqr[nx][ny]);
    }
  }
}

int main(int argc, char const *argv[]) 
{
  scanf("%d",&n);
  bfs();
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Ackers/p/10087563.html
今日推荐