HDOJ 1043 Eight(A* 搜索)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043

思路分析:

<1> 搜索算法: A*算法, Heuristic函数:曼哈顿距离

<2> 剪枝技巧: 如果8数码问题中的初始状态的逆序数为奇数(除了’x’),则不存在解;否则,存在解;

 

代码如下:

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int MAX_N = 362880 + 100;
int map[9];
bool close[MAX_N];
char dir[MAX_N], direction[] = "udlr";
int open[MAX_N], pa[MAX_N], f[MAX_N];
const int FACT[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
const int MOVE[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};

struct Node
{
    int id;
    int f, g, h;
    int flags;   // 表示该状态被松弛次数
    Node() {}
    Node(int a_id, int a_g, int a_h, int a_flags)
    {
        id = a_id, g = a_g, h = a_h, flags = a_flags;
        f = g + h;
    }
    friend bool operator<(const Node &lhs, const Node &rhs);
};
bool operator<(const Node &lhs, const Node &rhs)  { return lhs.f > rhs.f; }
priority_queue<Node> find_min;

int StateToCanto()
{
    int state_num = 1;
    for (int i = 0; i < 9; ++i)
    {
        int temp = map[i] - 1;

        for (int j = 0; j < i; ++j)
            temp -= (map[j] < map[i]);
        state_num += temp * FACT[8 - i];
    }
    return state_num;
}

int StateToId()
{
    int ans = 0;

    for (int i = 0; i < 9; ++i)
        ans = ans * 10 + map[i];
    return ans;
}

void IdToState(int num)
{
    int i = 8;
    while (num)
    {
        map[i--] = num % 10;
        num /= 10;
    }
}

int Heuristic()
{
    int sum = 0;
    for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
    {
        int k = i * 3 + j;
        if (map[k] == 9) continue;
        sum += abs(i - (map[k] - 1) / 3) + abs(j - (map[k] - 1) % 3);
    }
    return sum;
}

inline bool inversionNumberCheck()
{
    int cnt = 0;
    for (int i = 0; i < 9; ++i)
    {
        if (map[i] == 9) continue;
        for (int k = i - 1; k >= 0; --k)
        {
            if (map[k] == 9) continue;
            if (map[k] > map[i])
                cnt++;
        }
    }
    return cnt & 1;
}

void FindX(int &x, int &y)
{
    for (int i = 0; i < 9; ++i)
    {
        if (map[i] == 9)
        {
            y = i / 3;
            x = i % 3;
            return;
        }
    }
}

int A_star()
{
    int state_canto, state_id;

    state_canto = StateToCanto();
    state_id = StateToId();
    open[state_canto] += 1;
    Node start(state_id, 0, Heuristic(), open[state_canto]);
    f[state_canto] = start.f;
    find_min.push(start);
    pa[state_canto] = -1;
    dir[state_canto] = '0';

    if (state_id == 123456789)
        return state_canto;

    while (!find_min.empty())
    {
        Node parent = find_min.top();
        Node child;
        int p_x, p_y, c_x, c_y, parent_canto;
        find_min.pop();

        IdToState(parent.id);
        parent_canto = StateToCanto();
        if (parent.flags != open[parent_canto]) // 一个状态可能被松弛多次,检测parent是否为该状态最后一次松弛的状态
            continue;
        close[StateToCanto()] = 1; // To do

        FindX(p_x, p_y);
        for (int i = 0; i < 4; ++i)
        {
            int temp_swap, child_state_conto;

            c_x = p_x;
            c_y = p_y;
            c_x += MOVE[i][0];
            c_y += MOVE[i][1];

            if (c_x < 0 || c_x >= 3 || c_y < 0 || c_y >= 3)
                continue;
            temp_swap = map[p_x + p_y * 3];
            map[p_x + p_y * 3] = map[c_x + c_y * 3];
            map[c_x + c_y * 3] = temp_swap;
            child_state_conto = StateToCanto();

            if (close[child_state_conto] == 1)
            {
                temp_swap = map[p_x + p_y * 3];
                map[p_x + p_y * 3] = map[c_x + c_y * 3];
                map[c_x + c_y * 3] = temp_swap;

                continue;
            }

            child.id = StateToId();
            if (child.id == 123456789)
            {
                pa[child_state_conto] = parent_canto;
                dir[child_state_conto] = direction[i];
                return child_state_conto;
            }

            child.h = Heuristic();
            child.g = parent.g + 1;
            child.f = child.g + child.h;
            child.flags = open[child_state_conto] + 1;
            pa[child_state_conto] = parent_canto;
            dir[child_state_conto] = direction[i];
            if (open[child_state_conto] == 0 || f[child_state_conto] > child.f)
            {
                f[child_state_conto] = child.f;
                open[child_state_conto] = child.flags;
                find_min.push(child);
            }
            temp_swap = map[p_x + p_y * 3];
            map[p_x + p_y * 3] = map[c_x + c_y * 3];
            map[c_x + c_y * 3] = temp_swap;
        }
    }
    return -1;
}

void Find(int i)
{
    if (pa[i] == -1)
        return;
    else
    {
        Find(pa[i]);
        printf("%c", dir[i]);
    }
}

void PrintPath()
{
    int end_canto;

    for (int i = 0; i < 9; ++i)
        map[i] = i + 1;
    end_canto = StateToCanto();

    Find(end_canto);
    printf("\n");
}

int main()
{
    int ans = 0;

    while (scanf("%s", &map[0]) != EOF)
    {
        map[0] = (map[0] == 'x' ? 9 : (map[0] -= '0'));
        for (int i = 1; i < 9; ++i)
        {
            scanf("%s", &map[i]);
            map[i] = (map[i] == 'x' ? 9 : (map[i] -= '0'));
        }

        if (inversionNumberCheck())
        {
            printf("unsolvable\n");
            continue;
        }
        memset(open, 0, sizeof(open));
        memset(close, 0, sizeof(close));
        memset(pa, 0, sizeof(pa));
        memset(f, 0, sizeof(f));
        memset(dir, 0, sizeof(dir));
        ans = A_star();
        if (ans == -1)
            printf("unsolvable\n");
        else
            PrintPath();

        while (find_min.empty())
            find_min.pop();
    }
    return 0;
}
posted @ 2015-05-12 17:12  Leptus  阅读(284)  评论(0编辑  收藏  举报