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;
}
时间: 2024-08-05 13:03:43

HDOJ 1043 Eight(A* 搜索)的相关文章

BFS(八数码) POJ 1077 || HDOJ 1043 Eight

题目传送门1 2 题意:从无序到有序移动的方案,即最后成1 2 3 4 5 6 7 8 0 分析:八数码经典问题.POJ是一次,HDOJ是多次.因为康托展开还不会,也写不了什么,HDOJ需要从最后的状态逆向搜索,这样才不会超时.判重康托展开,哈希也可. POJ //#include <bits/stdc++.h> #include<iostream> #include<algorithm> #include<string> #include<stack

[hdoj]1244记忆化搜索

题解参考:http://www.cnblogs.com/peaceful-andy/archive/2012/08/14/2638907.html 很久很久以前……我一直以为记忆化搜索就是dp,dp就是记忆化搜索……多么悲哀的认识…… 动态规划是“填表格”的顺序依次递推,记忆化搜索说白了就是搜索的一种,树型,二者的相同点就是,边运行边记忆,对于计算过的都存起来,不再重复计算. 1 #include <iostream> 2 using namespace std; 3 4 const int

HDU 3567 BFS+预处理

HDU 1043的加强版 8数码问题 给出8数码问题的两种状态,求从A状态到B状态的最优解,数据保证有解,若有多解,输出最短且字典序最小的. 基本思路和1043的差不多,只不过这次要预处理出来9种情况的BFS 即: BFS(0,"012345678"); BFS(1,"102345678"); BFS(2,"120345678"); BFS(3,"123045678"); BFS(4,"123405678"

HDOJ 5323 Solve this interesting problem BFS搜索

BFS暴力搜索..... Solve this interesting problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 974    Accepted Submission(s): 263 Problem Description Have you learned something about segment tree?

HDU ACM 1043 Eight-&gt;广度优先搜索(BFS)+康托展开(全排列hash)实践

分析:经典的八数码问题,参考别人的代码写的,逆向广搜,把结果保存起来,之后在使用. #include<iostream> #include<queue> using namespace std; #define STATE_COUNT 363000 //因为9!=362880 int fact[]={1,1,2,6,24,120,720,5040,40320,362880}; //0到9的阶乘,用来计算hash值 char dir[4][2]={ {0,1},{0,-1},{1,0

HDU 1043 八数码(A*搜索)

在学习八数码A*搜索问题的时候需要知道以下几个点: Hash:利用康托展开进行hash 康托展开主要就是根据一个序列求这个序列是第几大的序列. A*搜索:这里的启发函数就用两点之间的曼哈顿距离进行计算就可以. 减枝:在八数码里,任意交换一个空行和一个位置的数字,这个八数码的逆序数是不变的,这样就可以根据目前状态判断是否可达终点状态了. 第一次做这个题用的map进行哈希,结果果断超时,之后又写了LRJ书上的hash方法也超时了,最后只能用康托展开了 详细请参考:[八数码的八重境界] http://

Hdoj 1428 A Walk Through the Forest 【spfa】+【记忆化搜索】

A Walk Through the Forest Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6397 Accepted Submission(s): 2348 Problem Description Jimmy experiences a lot of stress at work these days, especially sin

(中等) HDU 1043 Eight,经典搜索问题。

Problem Description 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

【HDOJ】1043 Eight

这道题目最开始做的时候wa+TLE.后面知道需要状态压缩,最近A掉.并且练习一下各种搜索算法. 1. 逆向BFS+康拓展开. 1 #include <iostream> 2 #include <queue> 3 #include <cstring> 4 #include <string> 5 #include <cstdio> 6 using namespace std; 7 8 #define N 9 9 #define MAXNUM 3628