POJ 1077 八数码 三种解法

本文介绍了POJ 1077题目的八数码问题,探讨了如何利用康托展开高效地表示和判断状态,并讨论了无解状态的剪枝策略。此外,提到了可以选择的搜索算法,如BFS、A*和IDA*。
摘要由CSDN通过智能技术生成

POJ 1077 Eight

题目链接:http://poj.org/problem?id=1077

有人说做搜索不做这道题,人生遗憾。我做完了之后,嗯,同意。

关于八数码问题,有以下几个问题需要想明白:

【状态表示和状态判断】
题目是按照连续9个字符的形式读入的,自然想到在node中用一个s[9]的数组存储当前状态。但是可以马上否认的是,我们不可能也用这个字符串来进行状态之间的比较,因为效率太低,操作麻烦,实现费劲。这个字符串一共有9!中排列方式,这里有一个定理:康托展开。很好地解决了存储和比较两个问题。通过康托展开,就可以把这9!个数字分别转换为一个int(注:9! = 362880 < 2^31-1)

【无解状态剪枝】
我们要的最后一个状态是{1, 2, 3, 4, 5, 6, 7, 8, x}, 这个排列的逆序数是0,是偶数。而对于任意一个排列,显然,因为x不表示数字,那么也就是说把x向左右两个方向移动并不会改变整个序列的逆序数,那么有结论:
如果初始序列的逆序数是一个奇数,则必然这个序列无解(必要,不充分)

有了以上两个想法,下一步就是选择搜索策略:
bfs可以,A*可以,IDA*也可以

【参考代码】

/**
 * Name: Eight 
 * P_ID: POJ 1077
 * Note: BFS + 康托展开
 * Date: 2016-05-18
 *
 */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 3;
const int cell = 362880;


int vis[cell];
int parent[cell];
char step[cell];

//cantor Base
const int fac[] = {
  1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
//dir
const int dir[4][2] = {
  {-1, 0}, {
  1,0}, {
  0, -1}, {
  0, 1}}; //u, d, l, r

struct node {
    char s[9];
    int space;
};

//康拓定理正向加密过程
int Hash(const char* str)
{
    int n = 9;
    int num = 0;
    int temp;
    for(int i=0; i<n-1; ++i)
    {
        temp=0;
        for(int j=i+1; j<n; j++)
        {
            if(str[j] < str[i])
                temp++;
        }
        num += fac[str[i]-1] * temp;
    }
    return num;
}

//康托定理逆向解码过程
void get_node(int num, node &temp)
{
    int n = 9;
    int a[9];
    for(int i=2; i<=n; ++i)
    {
        a[i-1] = num%i;
        num /= i;
        temp.s[i-1] = 0;
    }

    temp.s[0] = 0;
    int rn, i;
    for(int k=n; k>=2; --k)
    {
        rn = 0;
        for(i=n-1; i>=0; --i)
        {
            if(temp.s[i]!=0)
                continue;
            if(rn==a[k-1])
                break;
            ++rn;
        }
        temp.s[i] = k;
    }

    for(i=0; i<n; ++i)
    {
        if(temp.s[i]==0)
        {
            temp.s[i] = 1;
            break;
        }
    }
    temp.space = n - a[n-1] - 1;
}

//搜索
void bfs(const node& begin)
{
    memset(vis, 0, sizeof(vis));
    int u = Hash(begin.s);
    vis[u] = 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值