POJ 1077 Eight 八数码问题[康托展开 + BFS]

POJ 1077 Eight 八数码问题 [康托展开 + BFS]

对于八数码问题,可能问题的关键不是BFS,而是对状态的标记。八数码的状态恰好是一个全排列,那么对于全排列,康托展开就是一个完美的哈希。
康托展开:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! ,其中a[i]为当前未出现的元素中是排在第几个(从0开始)。这就是康托展开。
其实康托展开就是利用全排列的性质从高位向低位求第N大数或者第N小数。
#include <map>
#include <vector>
#include <queue>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

//#pragma comment(linker, "/STACK:1024000000,1024000000")

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define CASE(T)         for(scanf("%d",&T);T--;)
#define fst             first
#define snd             second

#define lson            l, mid, rt << 1
#define rson            mid+1, r, rt << 1 | 1

typedef __int64  LL;
//typedef long long LL;
typedef pair<int, int> PII;

const int MAXN = 9;
const int dir[] = { -3, -1, 1, 3};
const char H[] = "ulrd";
char src[MAXN], dest[MAXN];
const int MAXM = 1e6;
struct Node {
    int xpos, step;
    char grid[MAXN];
    string res;
    int contorV;
} cur, rear;

int T, R, C;

char str[MAXN];
queue<Node> Q;
int fact[10];
bool vis[MAXM];
void init() {
    fact[0] = 1;
    for(int i = 1; i < 10; i ++) {
        fact[i] = fact[i - 1] * i;
    }
}

/// contor展开求第N小数
int contor(const char s[]) {
    int ret = 0, temp;
    for(int i = 0; i < 9; i ++) {
        temp = 0;
        for(int j = i + 1; j < 9; j ++) {
            if(s[i] > s[j]) temp ++;
        }
        ret += temp * fact[9 - i - 1];
    }
    return ret + 1;
}

const int contor_dest = contor("123456789");

inline bool outRange(int x) {
    return x < 0 || x >= 9;
}
/// 除去不是从相邻位置过来的情况
inline bool check(int x1, int y1, int x2, int y2) {
    return ((x1 == x2) && abs(y1 - y2) == 1) || ((y1 == y2) && abs(x1 - x2) == 1);
}

int bfs() {
    memset(vis, false, sizeof(vis));
    int ret = -1;
    vis[cur.contorV = contor(cur.grid)] = true;
    cur.step = 0;
    Q.push(cur);
    while(!Q.empty()) {
        cur = Q.front();
        Q.pop();
        if(~ret) continue;
        if(cur.contorV == contor_dest) {
            ret = cur.step;
            cout << cur.res << endl;
            continue;
        }
        int x1, x2, y1, y2;
        x1 = cur.xpos / 3;
        y1 = cur.xpos % 3;
        for(int i = 0; i < 4; i++) {
            rear.xpos = cur.xpos + dir[i];
            x2 = rear.xpos / 3;
            y2 = rear.xpos % 3;
            if(outRange(rear.xpos)) continue;
            strcpy(rear.grid, cur.grid);
            swap(rear.grid[rear.xpos], rear.grid[cur.xpos]);
            if(!check(x1, y1, x2, y2)) continue;
            int val = rear.contorV = contor(rear.grid);
            if(vis[val]) continue;
            vis[val] = true;
            rear.step = cur.step + 1;
            rear.res = cur.res + H[i];
            Q.push(rear);
        }
    }
    return ret;
}

int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    init();

    while(~scanf("%s", str)) {
        cur.res = "";
        if(str[0] == 'x') {
            cur.xpos = 0;
            str[0] = '9';
        }
        cur.grid[0] = str[0];
        for(int i = 1; i < 9; i++) {
            scanf("%s", str);
            if(str[0] == 'x') {
                cur.xpos = i;
                str[0] = '9';
            }
            cur.grid[i] = str[0];
        }
        int ret = bfs();
        if(ret == -1) puts("unsolvable");
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值