八数码(bfs)

关于八数码问题的三种实现方式

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

typedef int State[9];
const int maxstate = 1000000;
State st[maxstate], goal;
int dist[maxstate];

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

bool inside(int x, int y) {
    return 0 <= x && x < 3 && 0 <= y && y < 3;
}

const int hashsize = 1000003;
int head[hashsize], next[maxstate];
void init_lookup_table() { memset(head,0,sizeof(head)); }
int hash(State & s) {
    int v = 0;
    for(int i = 0; i < 9; ++i) v = v*10 + s[i];
    return v % hashsize;
}
bool try_to_insert(int s) {
    int h = hash(st[s]);
    int u = head[h];
    while(u) {
        if(memcmp(st[u], st[s], sizeof(st[s])) == 0) return 0;
        u = next[u];
    }
    next[s] = head[h];
    head[h] = s; 
    return 1;
}

int bfs() {
    init_lookup_table();
    int front = 1, rear = 2;
    while(front < rear) {
        State & s = st[front];
        if(memcmp(goal,s,sizeof(s)) == 0) return front;
        int z;
        for(z = 0; z < 9; ++z) if(!s[z]) break;
        int x = z/3, y = z%3;
        for(int d = 0; d < 4; ++d) {
            int nx = x + dx[d];
            int ny = y + dy[d];
            int nz = nx*3 + ny;
            if(inside(nx,ny)) {
                State & t = st[rear];
                memcpy(&t,&s,sizeof(s));
                t[nz] = s[z];
                t[z] = s[nz];
                dist[rear] = dist[front] + 1;
                if(try_to_insert(rear)) ++rear; 
            } 
        }
        ++front;
    }
    return 0;
}

int main() {
    for(int i = 0; i < 9; ++i) scanf("%d",&st[1][i]);
    for(int i = 0; i < 9; ++i) scanf("%d",&goal[i]);
    int ans = bfs();
    if(ans > 0) printf("%d\n",dist[ans]);
    else printf("-1\n");
    return 0;
}

这段代码虽简短,但却有很多细节和小技巧。下面一一说明

1: 程序将八数码的状态单元int[9]直接用typedef替换为State,并定义状态队列st,最终目标goal,便于阅读和编码。

2: 在初始读入数据时,直接把初始状态读入队列的第一个'状态'中,将目标状态读入goal

3: bfs中采用内存控制函数memcpy赋值和memcmp判断,高效简洁。

4: 八数码属于隐式图搜索问题,因此要在普通bfs上增加一个"结点查找表"来判重。本程序采用自己设计的hash表来判重。下面还有两种实现方式,分别是"完美哈希"和STL中的set。

//完美hash
int vis[362880], fact[9];
void init_lookup_table() {
    fact[0] = 1;
    for(int i = 1; i < 9; ++i) fact[i] = fact[i-1] * i;
}
int try_to_insert(int s) {
    int code = 0;
    for(int i = 0; i < 9; ++i) {
        int cnt = 0;
        for(int j = i+1; j < 9; ++j) if(st[s][j] < st[s][i]) ++cnt;
        code += fact[8-i] * cnt;
    }
    if(vis[code]) return 0;
    return vis[code] = 1;
}
//STL
set<int> vis;
void init_lookup_table() { vis.clear(); }
int try_to_insert(int s) {
    int v = 0;
    for(int i = 0; i < 9; ++i) v = v*10 + st[s][i];
    if(vis.count(v)) return 0;
    vis.insert(v);
    return 1;
}
posted @ 2025-08-11 20:15  雾岛春颂  阅读(11)  评论(0)    收藏  举报