[HDU 1043] Eight A*或IDA*

http://acm.hdu.edu.cn/showproblem.php?pid=1043

题意:将八数码变成1 2 3 4 5 6 7 8 x

思路:就是普通的A*,IDA*, 用一个数组保存路径,vis数组保存移动方向, 还有开始要判断其实状态的逆序数和目标状态的逆序数是否一样。

求解八数码的时候IDA* 确实比 A* 要快,代码也短一点,可悲的是,这两个代码竟然都比一年前写的慢,难道我还退步了啊,算了 也懒得优化了。

这里写图片描述

IDA*代码(655ms):

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxn = 400000;
const char prt[4] = {'r', 'd', 'u', 'l'}; //方向输出
const int Move[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; //移动方向
const int fsh[10][2] = {{2, 2}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}};   //目标状态每个数的坐标,用于计算曼哈顿距离

char sta[105]; //保存路径

int abs(int a){
    return a < 0 ? -a : a;
}

int Manhattan(int *num) //曼哈顿距离
{
    int sum = 0;
    for(int i = 0; i < 9; i++){
        sum += abs(fsh[num[i]][0] - i/3) + abs(fsh[num[i]][1] - i%3);
    }
    return sum;
}

int isok(int *mapn)  //求逆序数
{
    int sum=0;
    for(int i = 0; i < 9; i++)
    {
        int num = 0;
        if(mapn[i] == 0)
            continue;
        for(int k = i+1; k < 9; k++)
        {
            if(mapn[i] > mapn[k] && mapn[k])
            {
                num++;
            }
        }
        sum += num;
    }
    return sum;
}

bool IdaStar(int *mapn, int x, int y, int pre, int step, int upper) //IDA*搜索
{
    int nx, ny;
    //四个方向
    for(int i = 0; i < 4; i++){
        nx = x + Move[i][0];
        ny = y + Move[i][1];
        if(pre + i == 3 || nx < 0 || nx > 2 || ny < 0 || ny > 2)  //回到前驱或者出界
            continue;
        mapn[3 * x + y] = mapn[3 * nx + ny];  //交换
        mapn[3 * nx + ny] = 0;
        int mht = Manhattan(mapn);  //计算曼哈顿距离(估价函数,相当于最少还要多少步)
        if(mht == 0){  //到达终点
            sta[step] = prt[i];
            sta[step+1] = '\0';
            return true;
        }
        if(mht + step <= upper){  //剪枝
            sta[step] = prt[i];
            if(IdaStar(mapn, nx, ny, i, step+1, upper))
                return true;
        }
        mapn[3 * nx + ny] = mapn[3 * x + y]; //交换回来
        mapn[3 * x + y] = 0;
    }
    return false;
}

int main()
{
    char str[10];
    while(cin>>str[0]){
        for(int i = 1; i < 9; i++){
            cin>>str[i];
        }
        int mapn[10], x, y;
        for(int i = 0; i < 9; i++){
            if(str[i] == 'x'){
                x = i / 3;
                y = i % 3;
                mapn[i] = 0;
            }
            else
                mapn[i] = str[i] - '0';
        }
        if(isok(mapn) % 2 == 1)
            cout<<"unsolvable"<<endl;
        else{
            int ans = Manhattan(mapn);
            if(ans == 0){
                cout<<endl;
            }
            else{
                int top = 0;
                while(++top){  //top表示搜索的上限
                    if(IdaStar(mapn, x, y, -1, 0, top)) //搜索成功
                    break;
                }
                cout<<sta<<endl;
            }
        }
    }
    return 0;
}

A*代码(1513ms):

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxn = 400000;
const char prt[4] = {'r', 'l', 'd', 'u'}; //方向
const int Move[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; //移动
const int dis[12] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800}; //计算康拓值的数组
const int fsh[10][2] = {{2, 2}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}}; //目标状态每个数的坐标,用于计算曼哈顿距离

struct node{
    int key;  //用康拓值保存图
    int x, y; // 0 的位置
    int step, mht; //step表示走了多少步,mht 表示最少还要多少步
    friend bool operator < (node a, node b){  //优先队列,b.step+b.mht 小的先出队
        return a.step+a.mht > b.step+b.mht;
    }
};

int pre[maxn]; //保存前驱
int vis[maxn]; //用于判重,顺便保存移动方向
priority_queue<node> que; //优先队列

inline int abs(int a)
{
    return a > 0 ? a : -a;
}

int Cantor(int *arg, int len) //康拓展开
{
    int resl = 1;
    for(int i = 0; i < len; i++){
        int counts = 0;
        for(int k = i + 1; k < len; k++){
            if(arg[i] > arg[k]){
                counts++;
            }
        }
        resl = resl + dis[len-i-1] * counts;
    }
    return resl;
}

void InvCantor(int *arg, int num, int len) // 逆康拓
{
    num = num - 1;
    bool vis[12] = {false};
    for(int i = 0; i < len; i++){
        int mid = 0;
        int cnt = num / dis[len-i-1];
        while(++mid){
            if(!vis[mid]){
                if(cnt == 0)
                    break;
                cnt--;
            }
        }
        arg[i] = mid - 1;
        vis[mid] = true;
        num = num % dis[len-i-1];
    }
}

int Manhattan(int *num) //曼哈顿距离
{
    int sum = 0;
    for(int i = 0; i < 9; i++){
        sum += abs(fsh[num[i]][0] - i/3) + abs(fsh[num[i]][1] - i%3);
    }
    return sum;
}

int isok(int *mapn) //求逆序数
{
    int sum=0;
    for(int i = 0; i < 9; i++)
    {
        int num = 0;
        if(mapn[i] == 0)
            continue;
        for(int k = i+1; k < 9; k++)
        {
            if(mapn[i] > mapn[k] && mapn[k])
            {
                num++;
            }
        }
        sum += num;
    }
    return sum;
}

int print(int rt)  //输出路径
{
    if(pre[rt] == -1){
        return 0;
    }
    print(pre[rt]);
    cout<<prt[vis[rt]];
}

int Astar(int *mapn, int x, int y) //A*搜索
{
    node w, r;
    w.step = 0;
    w.x = x, w.y = y;
    w.mht = Manhattan(mapn);
    w.key = Cantor(mapn, 9);
    vis[w.key] = 0;
    while(!que.empty()) que.pop();
    que.push(w);
    while(!que.empty())
    {
        r = que.top(), que.pop();
        InvCantor(mapn, r.key, 9);
        for(int i = 0; i < 4; i++){
            w.x = r.x + Move[i][0];
            w.y = r.y + Move[i][1];
            if(w.x < 0 || w.x > 2 || w.y < 0 || w.y > 2)
                continue;
            mapn[(r.x * 3 + r.y)] = mapn[(w.x * 3 + w.y)];  //移动 0 
            mapn[(w.x * 3 + w.y)] = 0;
            w.key = Cantor(mapn, 9); //计算康拓值
            w.mht = Manhattan(mapn); //计算曼哈顿距离
            if(vis[w.key] == -1){
                w.step = r.step + 1;
                que.push(w);
                vis[w.key] = i;
                pre[w.key] = r.key;
                if(w.mht == 0){
                    print(w.key);
                    cout<<endl;
                    return 0;
                }
            }
            mapn[(w.x * 3 + w.y)] = mapn[(r.x * 3 + r.y)]; 移动回来
            mapn[(r.x * 3 + r.y)] = 0;
        }
    }
    return -1;
}

int main()
{
    char str[10];
    while(cin>>str[0]){
        for(int i = 1; i < 9; i++){
            cin>>str[i];
        }
        int mapn[10], x, y;
        for(int i = 0; i < 9; i++){
            if(str[i] == 'x'){
                x = i / 3;
                y = i % 3;
                mapn[i] = 0;
            }
            else
                mapn[i] = str[i] - '0';
        }
        if(isok(mapn) % 2 == 1)
            cout<<"unsolvable"<<endl;
        else{
            int ans = Manhattan(mapn);
            if(ans == 0){
                cout<<endl;
            }
            memset(pre, -1, sizeof(pre));
            memset(vis, -1, sizeof(vis));
            Astar(mapn, x, y);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

achonor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值