八数码问题

        1.题目分析


        八数码问题是一个经典的BFS问题。BFS的从近到远的扩散过程,适合寻找最短路径。通过BFS解决八数码问题得到的距离就是最短路径。在分析题目的时候,可以看出,解决这题的关键在于如何“判重”,当我们在观察棋盘的时候,我们会发现每次上右下左移动的时候,可能会出现重复的棋盘状况,这是我们不希望看到的,复杂度会大大增加。而当我们使用暴力判重的时候,需要对棋盘9! = 362880个状态进行对比,这同样效率不高。通过查阅资料,本题可以使用数学方法“康托展开”(O(n^2))来判重。


        2.代码展示

  • 编辑器:Xcode Version 13.1

//
//  main.cpp
//  八数码问题(康托展开)
//
//  Created by Jkchou on 2022/1/22.
//

#include <iostream>
#include <queue>
using namespace std;
const int LEN = 362880;//排列状态最多的数量9!= 362880
struct node{
    int state[9];//记录一个八数码排列
    int dis;//记录到起点的距离
};
int dir[4][2] = {//左上右下四个方向
    {-1, 0},
    {0, -1},
    {1, 0},
    {0, 1}
};
int visited[LEN] = {0};//记录Cantor值,判重
int start[9];//开始状态
int goal[9];//目标状态
long int factory[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};//Cantor()用到的常数

bool Cantor(int str[], int n){//Cantor()展开判重
    long result = 0;
    for (int i=0; i < n; ++i) {
        int counted = 0;
        for (int j = i + 1; j < n; ++j) {
            if (str[i] > str[j]) {
                ++counted;
            }
        }
        result += counted * factory[n - i - 1];
    }
    if(!visited[result]){//没有访问过
        visited[result] = 1;
        return true;
    }
    else
        return false;
}

int BFS(){//深度优先搜索
    node head;
    memcpy(head.state, start, sizeof(head.state));
    head.dis = 0;
    queue<node> q;
    Cantor(head.state, 9);
    q.push(head);
    
    while (!q.empty()) {
        head = q.front();
        if (memcmp(head.state, goal, sizeof(goal)) == 0) {//找到目标状态,return
            return head.dis;
        }
        q.pop();
        int z;
        for (z = 0; z < 9; ++z) {//找到排列中0的位置
            if (head.state[z] == 0) {
                break;
            }
        }
        //转换为二维,左上角是原点(0, 0)
        int x = z % 3;//横坐标
        int y = z / 3;//纵坐标
        for (int i = 0; i < 4; ++i) {
            int newx = x + dir[i][0];
            int newy = y + dir[i][1];
            int nz = newx + 3 * newy;//转换为一维
            if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {
                node newnode;
                memcpy(&newnode, &head, sizeof(struct node));//复制这新的排列状态
                swap(newnode.state[z], newnode.state[nz]);//把0移动到新位置
                newnode.dis++;
                if (Cantor(newnode.state, 9)) {
                    q.push(newnode);
                }
            }
        }
    }
    return -1;
}

int main(){
    for (int i = 0; i < 9; ++i) {
        cin>>start[i];
    }
    for (int i = 0; i < 9; ++i) {
        cin>>goal[i];
    }
    int num = BFS();
    if (num != -1) {
        cout<<num<<endl;
    }else{
        cout<<"Impossible"<<endl;
    }
    
    return 0;
}

​​​​​​​


         3.备注


  1. 对于康托展开的原理及应用详解我的上篇博客就是,大家可以去看一下
  2. 八数码问题只有9!种状态,对于更大的问题,例如4 * 4棋盘的15数码问题,有16!种排列状态,如果仍然用数组存储,远远不够,此时需要更好的算法(例如双向广搜、A*、IDA*等)。
  3. 希望有一定BFS基础再做这题,会容易些。        
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值