POJ1077Eight

一. 题意:

八数码问题,输入一个3*3的棋盘。其中包含一个x和1到8,用x跟上下左右交换。求到满足状态12345678x时x要怎么移动。


二. 解题方法:

我是用康托展开和逆展开,把棋盘9!种不同的状态映射到0到9!-1个整数,然后用整数代表不同的状态,(暂且理解为一个整数代表一个状态,这好像就是传说中的hash算法吧)比如说123456789是一个状态,987654321是另外一个状态。然后一个状态一个状态搜索。

定义数组visited[]标记不同状态是否被访问过,我直接把x那个位置替换为9,然后从9开始搜索(BFS),定义一个结构体Path,用来存放路径。里面的direction用来表示前一个状态是从那个方向来到当前状态的,pre用来表示当前状态是从前面的哪个状态过来的。当遇见所代表的整数等于0,即所代表的棋盘为123456789,就退出。搜索完还没遇到等于0的情况就是没有解。(透露一个秘密:测试数据根本没有没解这种情况,因为一开始没仔细看题,直接猜样例然后就AC了哈哈哈哈)


三. 预备知识:

1. 组合数学的知识:

康托展开:对于一个全排列,将每一个排列又小到大排序,他们的排列顺序代表它们。给出它的排列,求出它第几大。举个例子比较好理解:

比如对于3个数123,它们的排列有123,132,213,231,312,321共6种,用0,1,2,3,4,5分别代表它们。给出一个312,让你求出4。怎么求呢。有点高中数学知识就够了比3小的数有2个。第一位可以是1, 2。第二位为2!然后看1,比1小的有0个。然后如果后面有继续加,只看后面的子序列,不要往回找。

康托逆展开:给数字求排列。从最高位开始,取整得出有几个数比它当前位数小,取余然后再取整,直接的逆运算,仔细想想就明白了。


四. 本来想写一篇长长的,可以解释得比较清楚的,毕竟这题想了好久,也学了好多啊感觉,然后好像也说不出什么了。毕竟我只用了单向BFS,就完了。只能说,详细看代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>

using namespace std;

const int MAXSIZE = 362880;

//The list of factorial:
int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};

//Mark whether this state have been tried:
bool visited[MAXSIZE];

//The direction of moving:
int movei[] = {-1, 1, 0, 0};
int movej[] = {0, 0, -1, 1};
char direction[] = {'u', 'd', 'l', 'r'};

struct Path
{
//how to move to current state(By up, down, left or right):
char direction;

//Remember how to reach current(from one state to another state):
int pre;

} path[MAXSIZE];

//Every state:
struct state
{
    int i, j;
    char grid[3][3];
};

//Whether it can be solve:
bool flag;

void order(char str[][3], int &ans)
{
    int i, j, k = 0, numOfMin;
    char temp[9];

    for(i = 0; i < 3; i++)
        for(j = 0; j < 3; j++){
            temp[k] = str[i][j];
            k++;
    }

    ans = 0;
    for(i = 0; i < 8; i++){
            numOfMin = 0;
        for(j = i + 1; j < 9; j++){
            if(temp[i] > temp[j]){
                numOfMin++;
            }
        }
        ans += fac[8 - i]*numOfMin;
    }

}

void getState(int num, state &ans)
{
    int i, j, k = 0, numOfMin;
    char temp[9];

    for(i = 8; i >= 0; i--){
        numOfMin = num/fac[i];
        temp[8-i] = '1' + numOfMin;
        num = num%fac[i];
    }

    for(i = 0; i < 3; i++)
        for(j = 0; j < 3; j++){
            ans.grid[i][j] = temp[k];
            k++;
            if('9' == ans.grid[i][j]){
                ans.i = i;
                ans.j = j;
            }
        }
}

void bfs(state start)
{
    queue <state> que;
    state now, next;
    int i, j, dir, nextOrder, curOrder;

    que.push(start);
    order(start.grid, curOrder);
    path[curOrder].pre = -1;
    visited[curOrder] = true;
    flag = true;

    while(!que.empty()){
        now = que.front();
        que.pop();
        order(now.grid, curOrder);

        for(dir = 0; dir < 4; dir++){
            i = now.i + movei[dir];
            j = now.j + movej[dir];
            if(i >= 0 && i < 3 && j >= 0 && j < 3){
                next = now;
                swap(next.grid[i][j], next.grid[next.i][next.j]);

                order(next.grid, nextOrder);
                if(!visited[nextOrder]){
                    visited[nextOrder] = true;
                    path[nextOrder].direction = direction[dir];
                    next.i = i, next.j = j;
                    path[nextOrder].pre = curOrder;
                    if(0 == nextOrder)
                        return;
                    que.push(next);
                }
            }
        }
    }

    flag = false;
}

void printPath()
{
    char way[1024];
    int k = 0, curOrder = 0, preOrder;

    preOrder = path[0].pre;

    for(k = 0; path[curOrder].pre != -1; k++){

        preOrder = path[curOrder].pre;

        way[k] = path[curOrder].direction;

        curOrder = preOrder;
    }

    for(k--; k >= 0; k--){
        cout<<way[k];
    }

    cout<<endl;
}
int main()
{
    //freopen("in.txt", "r", stdin);

    int i, j;

    state start;

    for(i = 0; i < 3; i++)
        for(j = 0; j < 3; j++){

        cin>>start.grid[i][j];

        if('x' == start.grid[i][j]){
            start.i = i;
            start.j = j;
            start.grid[i][j] = '9';
            }
        }

    bfs(start);
    if(flag)
        printPath();
    else
        cout<<"unsolvable\n";
}

G++250ms。

回头来看,康托逆展开写错了,仔细看下代码,根本没用过!- -。


你可能感兴趣的:(POJ1077Eight)