poj 1077(8数码)

来源:互联网 发布:网络变压器维修 编辑:程序博客网 时间:2024/05/14 05:36

        8数码,又称九宫格,应该是大家都玩过的一种游戏。在一个3*3的棋盘上放有8个棋子,棋子可以上下左右移动,要求通过移动棋子,使棋盘从一种状态转换为另一种状态。

    首先,明显地,这是一个搜索问题,共有9!种状态,并不算多,使用普通BFS或双向BFS就能解决,其次,为了保存状态,可以使用康托展开,这样能减省许多空间。

    普通BFS在状态空间搜索时,搜索了许多无用的状态,导致了时间的浪费,因此,可以使用A*或IDA*。

    使用A*的话,可以根据当前状态处于正确位置的棋子数目来建立启发函数,而IDA*,也就是迭代加深的A*算法,是指先指定搜索深度,然后用DFS进行搜索,若找不到目标状态的话,则增加搜索深度,直到找到目标状态为止。因此,IDA*也是深度受限的DFS,其效率要比A*还要高。

    另外,初始状态能转变为目标状态的前提是,两者逆序数的奇偶性一致。可以简要的证明一下:

    1:当x在某一行里移动时,不会改变该状态逆序数的奇偶性(很明显,因为序列的排列根本没变,x可是不计入排列的);

    2:当x与另外一行的数字交换位置时,相当于该数字连续移动了两次,而这样同样不会影响逆序数的奇偶性。

代码如下:

  A*

#include <cstdio>#include <stack>#include <set>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>#include <string>#include <map>#include <iomanip>#include <cmath>#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define F first#define S second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define BitCount(x) __builtin_popcount(x)#define BitCountll(x) __builtin_popcountll(x)#define LeftPos(x) 32 - __builtin_clz(x) - 1#define LeftPosll(x) 64 - __builtin_clzll(x) - 1const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;using namespace std;const double eps = 1e-8;const int MAXN = 300 + 10;const int MOD = 1000007;const int M=20010;const int N=500010;const int d[][2] = { {0,1},{0,-1},{-1,0},{1,0} };typedef pair<int, int> pii;int f[9];bool vis[N];char dir[4]={ 'r','l','u','d' };struct node{    int state,step,pre;    // 状态, 步骤,父亲结点    int f,h;    char c;}dis[N];struct cmp{    bool operator()(node a,node b)    {        return a.f>b.f;    }};int getX(const char g[]){    for (int i=0;i<9;i++) if (g[i]=='9') return i;}int getH(const char g[])  {    int sum=0;    for (int i=0;i<9;i++) {        int t=g[i]-'1';        sum+=abs(t/3-i/3)+abs(t%3-i%3); // 该位置的数字与应该在这个位置的数字的横纵坐标差值之和    }    return sum;}bool niu(char s[]) // 判断是否有解{    int i,j,cnt=0;    for (i=0;i<9;i++)    {        if (s[i]=='9') continue;        for (j=i+1;j<9;j++) {            if (s[j]=='9') continue;            if (s[i]>s[j]) cnt++;        }    }    if (cnt&1) return false;    else return true;}int KT(const char s[])  // 康托展开{    int i,j,sum=0;    for (i=0;i<9;i++) {        int t=0;        for (j=i+1;j<9;j++) if (s[i]>s[j]) t++;        sum+=t*f[8-i];    }    return  sum;}void invKT(char s[],int state) // 康托逆展开{    int i,j;    bool a[10];    MS(a,false);    //state--;    for (i=0;i<9;i++) {        int t=state/f[8-i] ;        for (j=0;j<9;j++) if (!a[j]) {            if (!t) break;            t--;        }        s[i]=j+'1';        a[j]=true;        state%=f[8-i];    }    s[i]='\0';}bool astar(int state){    int i,j;    char s[10];    node t;    t.state=state;    t.step=0;    t.pre=-1;    dis[state]=t;    priority_queue<node,vector<node>,cmp> q;    q.push(t);    MS(vis,false);    vis[state]=true;    while(!q.empty()){        t=q.top(); q.pop();        if (!t.state) return true;        invKT(s,t.state);        int pos=getX(s);        int x=pos/3,y=pos%3;        for (i=0;i<4;i++){            int xx=x+d[i][0];            int yy=y+d[i][1];            if (xx>=0 && xx<3 && yy>=0 && yy<3) {                swap(s[x*3+y],s[xx*3+yy]);                int temp=KT(s);                if (!vis[temp]) {                    node t2;                    t2.state=temp;  // 结点自身的状态                    t2.step=t.step+1;                    t2.h=getH(s);                    t2.f=t2.h+t2.step;                    t2.pre=t.state;  // 父亲结点的状态                    t2.c=dir[i];  // 方向                    dis[temp]=t2;                    q.push(t2);                    vis[temp]=true;                }                swap(s[x*3+y],s[xx*3+yy]);            }        }    }    return false;}void print(int state){    if (dis[state].pre==-1) return;    print(dis[state].pre);    printf("%c",dis[state].c);}int main(){    int i,j;    char ch,s[10];    f[0]=f[1]=1;    for (i=2;i<=9;i++) f[i]=f[i-1]*i;    while(cin>>ch)    {        if (ch=='x') s[0]='9';        else s[0]=ch;        for (i=1;i<9;){            scanf("%c",&ch);            if (isdigit(ch)) s[i++]=ch;            else if (ch=='x') { s[i++]='9';  }        }        s[i]='\0';        int t=KT(s);        if (niu(s) && astar(t)) {            print(0); cout<<endl;        }        else puts("unsolvable");    }}

IDA*

#include <cstdio>#include <stack>#include <set>#include <iostream>#include <string>#include <vector>#include <queue>#include <functional>#include <cstring>#include <algorithm>#include <cctype>#include <string>#include <map>#include <iomanip>#include <cmath>#define LL long long#define ULL unsigned long long#define SZ(x) (int)x.size()#define Lowbit(x) ((x) & (-x))#define MP(a, b) make_pair(a, b)#define MS(arr, num) memset(arr, num, sizeof(arr))#define PB push_back#define F first#define S second#define ROP freopen("input.txt", "r", stdin);#define MID(a, b) (a + ((b - a) >> 1))#define LC rt << 1, l, mid#define RC rt << 1|1, mid + 1, r#define LRT rt << 1#define RRT rt << 1|1#define BitCount(x) __builtin_popcount(x)#define BitCountll(x) __builtin_popcountll(x)#define LeftPos(x) 32 - __builtin_clz(x) - 1#define LeftPosll(x) 64 - __builtin_clzll(x) - 1const double PI = acos(-1.0);const int INF = 0x3f3f3f3f;using namespace std;const double eps = 1e-8;const int MAXN = 300 + 10;const int MOD = 1000007;const int M=20010;const int N=500010;const int d[][2] = { {0,1},{0,-1},{-1,0},{1,0} };typedef pair<int, int> pii;int f[9],path[N];bool vis[N];int pathLen,limit;int next[9][4]= //  行表示x在九宫格的位置,4列表示4个方向,表示移动后的位置,-1表示越界{    {-1,3,1,-1},    {0,4,2,-1},    {1,5,-1,-1},    {-1,6,4,0},    {3,7,5,1},    {4,8,-1,2},    {-1,-1,7,3},    {6,-1,8,4},    {7,-1,-1,5}};inline int getX(const char g[]){    for (int i=0;i<9;i++) if (g[i]=='9') return i;}int getH(const char g[]){    int sum=0;    for (int i=0;i<9;i++) {        int t=g[i]-'1';        sum+=abs(t/3-i/3)+abs(t%3-i%3); // 该位置的数字与应该在这个位置的数字的横纵坐标差值    }    return sum;}int KT(const char s[]){    int i,j,sum=0;    for (i=0;i<9;i++) {        int t=0;        for (j=i+1;j<9;j++) if (s[i]>s[j]) t++;        sum+=t*f[8-i];    }    return  sum;}int niu(const char s[]){    int i,j,cnt=0;    for (i=0;i<9;i++)    {        if (s[i]=='9') continue;        for (j=i+1;j<9;j++) {            if (s[j]=='9') continue;            if (s[i]>s[j]) cnt++;        }    }    return cnt;}bool ID_Astar(char s[],int len,int x){    int i,j;    int state=KT(s);    if (len<=limit){        if (state==0) {            pathLen=len;            return true;        }    }else return false; // over the limit    for (i=0;i<4;i++){        if (next[x][i]==-1) continue;  // 不能向该方向移动        if (len>0 && abs(i-path[len-1])==2) continue;  //  不向上一次的相反方向移动        swap(s[x],s[next[x][i]]);        int t=getH(s);        path[len]=i;        if (t+len<=limit && ID_Astar(s,len+1,next[x][i])) return true;        swap(s[x],s[next[x][i]]);    }    return false;}void print(){    for (int i=0;i<pathLen;i++){        switch(path[i])        {        case 0:            printf("l"); break;        case 1:            printf("d"); break;        case 2:            printf("r"); break;        case 3:            printf("u"); break;        }    }}int main(){    int i,j;    char ch,s[10];    f[0]=f[1]=1;    for (i=2;i<=9;i++) f[i]=f[i-1]*i;    while(cin>>ch)    {        if (ch=='x') s[0]='9';        else s[0]=ch;        for (i=1;i<9;){            scanf("%c",&ch);            if (isdigit(ch)) s[i++]=ch;            else if (ch=='x') { s[i++]='9';  }        }        s[i]='\0';        int t=getX(s);        limit=getH(s);        if ((niu(s)&1)==0) {            while (!ID_Astar(s,0,t)) limit++;            print(); cout<<endl;           //puts("Y");        }        else puts("unsolvable");    }}



0 0