[HDU 1043] Eight A*或IDA*

来源:互联网 发布:淘宝客qq群如何拉人 编辑:程序博客网 时间:2024/05/22 17:11

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;}
0 0