kuangbin二A:Eight(八数码问题)(A*搜索+康托展开hash)

Describe:
The 15-puzzle has been around for over 100 years; even if you don’t know it by that name, you’ve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let’s call the missing tile ‘x’; the object of the puzzle is to arrange the tiles so that they are ordered as:

 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15  x

_
where the only legal operation is to exchange ‘x’ with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:

 1  2  3  4     1  2  3  4     1  2  3  4     1  2  3  4
 5  6  7  8     5  6  7  8     5  6  7  8     5  6  7  8
 9  x 10 12     9 10  x 12     9 10 11 12     9 10 11 12
13 14 11 15    13 14 11 15    13 14  x 15    13 14 15  x
            r->            d->            r->

_
The letters in the previous row indicate which neighbor of the ‘x’ tile is swapped with the ‘x’ tile at each step; legal values are ‘r’,’l’,’u’ and ‘d’, for right, left, up, and down, respectively.
_
Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing ‘x’ tile, of course).
_
In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.

_
Input
You will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus ‘x’. For example, this puzzle

1 2 3
x 4 6
7 5 8

is described by this list:

1 2 3 x 4 6 7 5 8

Output
You will print to standard output either the word “unsolvable”, if the puzzle has no solution, or a string consisting entirely of the letters ‘r’, ‘l’, ‘u’ and ‘d’ that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases.
Sample Input

2  3  4  1  5  x  7  6  8

Sample Output

ullddrurdllurdruldr

曾经有位大牛说过,没做过八数码问题的人生不是一个完整的人生,等我认认真真做完一遍之后,不管别人信不信,反正我信了┓(;´_`)┏。

这道题我用了康托展开这样一个小技巧,它是一种hash函数,是数列和数字之间的双映射函数,相当于对一个数列进行全排列,不同的排列方式之间用一个方法给他们排个序,每一个排列对应的序号数就是这个排列(数列)与这个数字之间的hash映射。公式如下:

设有一个n位的每一位均不同的数列,则其对应的hash值X为:
X = a[n] * (n-1)! + a[n-1] * (n-2)! + … + a[i] * (i-1)! + … + a[1] * 0!
其中i为第i位,a[i]为数列中第i位右边(即比i低的位)比第i位数小的数字的个数。

这样就可以将一个数列,更进一步地,一个字符串,压缩成一个整数,大大节省了空间,在对两个数列判重的时候也不用一位一位地去判断了,节省了计算量。

而这道题主体的思想(其中一种,当然其他解法也有很多)就是A*搜索,他是一种广泛用于AI及游戏开发的一类搜索算法,我才疏学浅,估计讲不明白,贴一个个人认为比较好的连接:
https://blog.csdn.net/coutamg/article/details/53923717

这样综合应用一下就解出了这道八数码,HDU结果:
这里写图片描述

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <queue>
typedef long long ll;
using namespace std;

const int MAXN = 370000;
const int aim = 46233;
int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int vis[MAXN];
char index[] = {'l', 'u', 'r', 'd'};
int mov[] = {-1, -3, 1, 3};

struct node
{
    int s[15];
    int zero, Hash = -1;
    string path;
    int h, g;
};

bool operator<(node a, node b)
{
    return a.h!=b.h ? a.h>b.h : a.g>b.g;
}

void Swap(int& a, int& b)
{
    int tmp;
    tmp = a;
    a = b;
    b = tmp;
}

int cantor(int s[])
{
    int sum = 0;
    for(int i = 0; i < 9; i++)
    {
        int cnt = 0;
        for(int j = i + 1; j < 9; j++)
            if(s[j] < s[i]) cnt++;
        sum += cnt * fac[9-i-1];
    }
    return sum;
}

bool ifUnsolvable(int s[])
{
    int cnt = 0;
    for(int i = 0; i < 9; i++)
        if(s[i])
            for(int j = i + 1; j < 9; j++)
                if(s[j] < s[i] && s[j]) cnt++;
    if(cnt % 2) return true;
    return false;
}

int geth(int s[])
{
    int sum = 0;
    for(int i = 0; i < 9; i++)
    {
        int x1, y1, x2, y2;
        if(s[i] != 0)
        {
            y1 = ((i+1)+2)/3;
            y2 = (s[i]+2)/3;
            x1 = ((i+1)+2)%3 + 1;
            x2 = (s[i]+2)%3 + 1;
            sum += (abs(x1-x2) + abs(y1-y2));
        }
    }
    return sum;
}

void init(node cur)
{
    memset(vis, -1, sizeof(vis));
    cur.Hash = cantor(cur.s);
    cur.h = geth(cur.s);
    cur.g = 0;
    vis[cur.Hash] = 1;
}

bool check(node c, int i)
{
    if(c.zero-2<=0 && i==1) return false;
    if(c.zero+2>=8 && i==3) return false;
    if((c.zero+3)%3==0 && i==0) return false;
    if((c.zero+1)%3==0 && i==2) return false;
    return true;
}

void Astar(node IN)
{
    priority_queue<node> OPEN;
    node cur, opt;
    cur = IN;
    init(cur);
    OPEN.push(cur);
    while(!OPEN.empty())
    {
        cur = OPEN.top();
        OPEN.pop();
        for(int i = 0; i < 4; i++)
        {
            opt = cur;
            if(check(opt, i))
            {
                int tmp = opt.zero;
                int mv = mov[i];
                int a = opt.s[tmp], b = opt.s[tmp+mv];
                Swap(a, b);
                opt.s[tmp] = a, opt.s[tmp+mv] = b;
                opt.zero += mv;
                opt.Hash = cantor(opt.s);
                if(opt.Hash == cur.Hash) continue;
                if(vis[opt.Hash] == -1)
                {
                    vis[opt.Hash] = 1;
                    opt.g = cur.g + 1;
                    opt.h = geth(opt.s);
                    opt.path += index[i];
                    OPEN.push(opt);
                }
                if(opt.Hash == aim)
                {
                    cout << opt.path << endl;
                    return;
                }
            }
        }
    }
    printf("unsolvable\n");
}

int main()
{
    node in;
    char ch[100];
    while(gets(ch))
    {
        memset(in.s, 0, sizeof(in.s));
        in.path.clear();
        int l = strlen(ch);
        int j = 0;
        for(int i = 0; i < l; i++)
        {
            if(ch[i]>='0' && ch[i]<='9')
                in.s[j++] = ch[i] - '0';
            else if(ch[i]=='x')
            {
                in.zero = j;
                in.s[j++] = 0;
            }
        }
        in.Hash = cantor(in.s);
        if(in.Hash == aim)
        {
            printf("\n");
            continue;
        }
        else if(ifUnsolvable(in.s))
            printf("unsolvable\n");
        else
            Astar(in);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Izayoi_w/article/details/81209336
今日推荐