八数码难题

八数码难题

 

 我们看到这个题的第一感觉就是BFS,显然BFS可做

经过我坚持不懈的努力发现了问题所在

我们发现,这个题具有一定的奇特性质:

既知道起始状态也知道终点状态

我们就想到了运用双向BFS的内容(一开始也我也不会)

大体是记录空格位置和棋盘状态,加入队列不断增加节点入队。同时注意判重,将棋盘转换为九位数(如果第一个数字为0则是八位数),用hash表解决

双向宽搜,顾名思义就是从两边搜,适用于已知起始状态和目标状态,求最短步骤的题目。

我们可以开两类数组,分别表示正方方向搜索的队列,然后初始,目标状态分别入对应队列,进行扩展节点,直到两个方向搜索相遇时即得出最短步骤。

出于优化时间的目的,我们往往先搜队列中节点少的方向,轮流进行。

具体讲就是:两个方向根据队列中节点数交替扩展节点,每扩展一个节点为,在本队列判重后还要在另一个方向的队列中找是否出现,出现说明相遇,输出最短步骤为正方方向扩展到该节点所需最短步骤。

注意双向广搜时,反方向扩展节点时操作与正方向相反,比如向左移动要变为向右移动,但这点对本题没有影响。

这里找了一份比较好看的代码,相对来说比较容易理解

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
const int las=123804765;//状态压缩终状态
//空格与右、左、上、下交换
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
int ma[4][4];//现状态地图
int beg;//初始状态
int zero_x,zero_y;//零的位置
int go_x,go_y;//要交换的位置

//开一个876543211的数组肯定compile error,因此用map记录
map<int,int> dir;
map<int,int> ans;
queue<int> qu;
//双向bfs可节省大量时间
//其使用要求是知道最终状态,适合此题

void pre_work()
{
    scanf("%d",&beg);
    if(beg==las)
    {
        printf("%d\n",0);
        exit(0);
    }
    qu.push(beg);
    qu.push(las);
    //将两头的bfs区分为不同方向
    ans[beg]=0;
    ans[las]=1;
    dir[beg]=1;
    dir[las]=2;
}

void work()
{
    int now,cur;
    while(!qu.empty())
    {
        cur=now=qu.front();
        qu.pop();
        //将now状态分到地图中
        for(int i=3;i>0;i--)
            for(int j=3;j>0;j--)
            {
                ma[i][j]=now%10;
                now/=10;
                if(!ma[i][j])
                {
                    zero_x=i;
                    zero_y=j;
                }
            }
        //空格尝试与上下左右交换
        for(int i=0;i<4;i++)
        {
            go_x=zero_x+dx[i];
            go_y=zero_y+dy[i];
            if(go_x<1||go_x>3||go_y<1||go_y>3) continue;
            swap(ma[zero_x][zero_y],ma[go_x][go_y]);
            now=0;
            //重新将交换后的状态记录为now
            for(int i=1;i<=3;i++)
                for(int j=1;j<=3;j++)
                    now=10*now+ma[i][j];
            //判重:来回走,舍弃
            if(dir[now]==dir[cur])
            {
                //注意要先交换回来
                swap(ma[zero_x][zero_y],ma[go_x][go_y]);
                continue;
            }
            //如果搜索到了另一边已经搜到的状态
            if(dir[now]+dir[cur]==3)
            {
                printf("%d\n",ans[cur]+ans[now]);
                return;
            }
            //方向不变,路程加一
            ans[now]=ans[cur]+1;
            dir[now]=dir[cur];
            swap(ma[zero_x][zero_y],ma[go_x][go_y]);
            qu.push(now);
        }
    }
}

int main()
{
    pre_work();
    work();
    return 0;
}

 

posted @ 2020-04-11 11:07  卍GC卐  阅读(284)  评论(0编辑  收藏  举报