POJ 1077 HDU 1043 Eight (IDA*)

题意就不用再说明了吧......如此经典

之前想用双向广搜、a*来写,但总觉得无力,现在用IDA*感觉其他的解法都弱爆了..............想法活跃,时间,空间消耗很小,给它跪了


启发式搜索关键还是找估价函数:此题估价函数可大致定性为每个数字(除去x,只要8个数字)当前位置与它期望位置的曼哈顿距离

即为:v += abs(i - pos[map[i][j] - 1][0]);     v += abs(j - pos[map[i][j] - 1][1]);     大致估算为几十步内得出结果。

 

#include <iostream>

#include <algorithm>

#include <cmath>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <vector>

#include <set>

#include <queue>

#include <stack>

#include <climits>//形如INT_MAX一类的

#define MAX 100005

#define INF 0x7FFFFFFF

#define REP(i,s,t) for(int i=(s);i<=(t);++i)

#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

#define mp(a,b) make_pair(a,b)

#define L(x) x<<1

#define R(x) x<<1|1

# define eps 1e-5

//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂

using namespace std;



int pos[9][2] = {  //各个数字的初始位置

    {0,0},{0,1},{0,2},

    {1,0},{1,1},{1,2},

    {2,0},{2,1},{2,2}

};

int map[4][4];

int buff[50];

char input[11];

int limit,ok;

int dx[] = {-1,0,1,0}; //u r d l____0 1 2 3

int dy[] = {0,1,0,-1};

char op[] = {'u','r','d','l'};



int h(int x,int y) {

    int v = 0;

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

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

            if(i != x || j != y) {

                v += abs(i - pos[map[i][j] - 1][0]);

                v += abs(j - pos[map[i][j] - 1][1]);

            }

        }

    }

    return v;

}



int dfs(int x,int y,int step,int pre) {

    int hn = h(x,y);

    if(hn == 0) {

        ok = 1;

        return step;

    }

    if(hn + step > limit) return hn + step;

    int minn = INF;

    for(int i=0; i<4; i++) {

        if(abs(i - pre) == 2) continue;

        int xx = x + dx[i];

        int yy = y + dy[i];

        if(xx<0 || xx >=3 || yy<0 || yy >=3) continue;

        buff[step] = i;

        swap(map[x][y],map[xx][yy]);

        int tmp = dfs(xx,yy,step+1,i);

        if(ok) return tmp;

        minn = min(tmp,minn);

        swap(map[x][y],map[xx][yy]);

    }

    return minn;

}



void IDA_star(int x,int y) {

    ok = 0;

    memset(buff,-1,sizeof(buff));

    while(ok == 0 && limit <= 36) {

        limit = dfs(x,y,0,-111);

    }

    if(ok == 1) {

        for(int i=0; i<limit; i++) printf("%c",op[buff[i]]);

        puts("");

    } else puts("unsolvable");

}



int main() {

    for(int i=0; i<9; i++) cin >> input[i];

    int t = 0,x,y;

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

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

            if(input[t] == 'x') {

                map[i][j] = 9;

                x = i;

                y = j;

                t++;

            } else map[i][j] = input[t++] - '0';

        }

    }

    int limit = h(x,y);

    if(limit == 0) {

        puts("");

        return 0;

    }

    IDA_star(x,y);

    return 0;

}

 

想吐槽一下杭电的这题.........估计各种无法到达目标的数据,所以在输入时候通过求逆序数对数来判断是否有解,本来是TLE,一下蹦到171ms

 

#include <iostream>

#include <algorithm>

#include <cmath>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <vector>

#include <set>

#include <queue>

#include <stack>

#include <climits>//形如INT_MAX一类的

#define MAX 100005

#define INF 0x7FFFFFFF

#define REP(i,s,t) for(int i=(s);i<=(t);++i)

#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

#define mp(a,b) make_pair(a,b)

#define L(x) x<<1

#define R(x) x<<1|1

# define eps 1e-5

//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂

using namespace std;



int pos[9][2] = {  //各个数字的初始位置

    {0,0},{0,1},{0,2},

    {1,0},{1,1},{1,2},

    {2,0},{2,1},{2,2}

};

int map[4][4];

int buff[50];

char input[11];

int limit,ok;

int dx[] = {-1,0,1,0}; //u r d l____0 1 2 3

int dy[] = {0,1,0,-1};

char op[] = {'u','r','d','l'};



int h(int x,int y) {

    int v = 0;

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

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

            if(i != x || j != y) {

                v += abs(i - pos[map[i][j] - 1][0]);

                v += abs(j - pos[map[i][j] - 1][1]);

            }

        }

    }

    return v;

}



int dfs(int x,int y,int step,int pre) {

    int hn = h(x,y);

    if(hn == 0) {

        ok = 1;

        return step;

    }

    if(hn + step > limit) return hn + step;

    int minn = INF;

    for(int i=0; i<4; i++) {

        if(abs(i - pre) == 2) continue;

        int xx = x + dx[i];

        int yy = y + dy[i];

        if(xx<0 || xx >=3 || yy<0 || yy >=3) continue;

        buff[step] = i;

        swap(map[x][y],map[xx][yy]);

        int tmp = dfs(xx,yy,step+1,i);

        if(ok) return tmp;

        minn = min(tmp,minn);

        swap(map[x][y],map[xx][yy]);

    }

    return minn;

}

int canget(int a[4][4]) { //这一步省了好多时间 求逆序数对数

    int i,j,sum=0,b[10],k=0;

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

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

            if(a[i][j] != 9)

                b[++k]=a[i][j];

        }

    }

    for(i=1; i<=8; i++) {

        for(j=1; j<i; j++) {

            if(b[i]<b[j])sum++;

        }

    }

    if(sum % 2 ==0)return 1;

    else return 0;

}

void IDA_star(int x,int y) {

    ok = 0;

    memset(buff,-1,sizeof(buff));

    while(ok == 0 && limit <= 30) {

        limit = dfs(x,y,0,-111);

    }

    if(ok == 1) {

        for(int i=0; i<limit; i++) printf("%c",op[buff[i]]);

        puts("");

    } else puts("unsolvable");

}



int main() {

    while(cin >> input[0]) {

        //memset(map,0,sizeof(map));

        for(int i=1; i<9; i++) cin >> input[i];

        int t = 0,x,y;

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

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

                if(input[t] == 'x') {

                    map[i][j] = 9;

                    x = i;

                    y = j;

                    t++;

                } else map[i][j] = input[t++] - '0';

            }

        }

        limit = h(x,y);

        if(limit == 0) {

            puts("");

            continue;

        }

        if(canget(map))

            IDA_star(x,y);

        else puts("unsolvable");

    }

    return 0;

}


经过猥琐的测试,limit最小限制在29步....................


 

 

你可能感兴趣的:(poj)