poj 1077(8数码)

本文详细介绍了8数码游戏的求解策略,包括使用普通BFS、A*、IDA*等算法。重点阐述了如何利用康托展开优化状态存储,以及初始状态与目标状态解的存在条件。通过代码实现展示了A*与IDA*算法在解决8数码问题的应用。

        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) - 1
const 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) - 1
const 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");
    }
}






















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值