zoukankan      html  css  js  c++  java
  • 八数码难题

    八数码难题

     我们看到这个题的第一感觉就是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
    ",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
    ",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;
    }
  • 相关阅读:
    《MIT 6.828 Lab 1 Exercise 8》实验报告
    《MIT 6.828 Lab 1 Exercise 7》实验报告
    《MIT 6.828 Lab 1 Exercise 4》实验报告
    《MIT 6.828 Lab 1 Exercise 3》实验报告
    《MIT 6.828 Lab 1 Exercise 2》实验报告
    《Brennan's Guide to Inline Assembly》学习笔记
    《PC Assembly Language》读书笔记
    《MIT 6.828 Lab1: Booting a PC》实验报告
    MIT 6.828 课程介绍
    《Data Structures and Algorithm Analysis in C》学习与刷题笔记
  • 原文地址:https://www.cnblogs.com/gongcheng456/p/12678258.html
Copyright © 2011-2022 走看看