【BFS / DBFS / A* / IDA*】——POJ1077——八数码问题

                                                 Eight

Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 29472   Accepted: 12811   Special Judge

Description

The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as: 
 1  2  3  4 

5 6 7 8
9 10 11 12
13 14 15 x

where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle: 
 1  2  3  4    1  2  3  4    1  2  3  4    1  2  3  4 

5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8
9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12
13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x
r-> d-> r->

The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively. 

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and 
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course). 

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three 
arrangement. 

Input

You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle 
 1  2  3 

x 4 6
7 5 8

is described by this list: 

1 2 3 x 4 6 7 5 8

Output

You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

Sample Input

 2  3  4  1  5  x  7  6  8 

Sample Output

ullddrurdllurdruldr


题意:解决八数码问题并打印路径

思路:

一、BFS+康托展开判重

康托展开:(康托展开就是一种特殊的哈希函数)

        把一个整数X展开成如下形式:
        X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0![1] 
        其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]<i(1<=i<=n)
补充:康托展开的逆运算可求全排列(见课件)
 
枚举路径并记入结构体
 
代码如下:704ms
#include<stdio.h>
#include<queue>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN=1000000;

int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//康拖展开判重
//            0!1!2!3!4!  5!   6!   7!     8!       9!

bool vis[MAXN];//标记

int cantor(int s[])//康拖展开求该序列的hash值
{
    int sum=0;
    for(int i=0;i<9;i++)
    {
        int num=0;
        for(int j=i+1;j<9;j++)
          if(s[j]<s[i])num++;
        sum+=(num*fac[9-i-1]);
    }
    return sum+1;
}

struct Node
{
    int s[9];
    int loc;//“0”的位置,把“x"当0
    int status;//康拖展开的hash值
    string path;//路径
}ncur;

string path;
int aim=46234;//123456780对应的康拖展开的hash值
int move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r
char indexs[5]="udlr";//正向搜索

bool bfs()
{
    memset(vis,false,sizeof(vis));
    Node cur,next;
    queue<Node>q;
    q.push(ncur);

    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(cur.status==aim)
        {
            path=cur.path;
            return true;
        }

        int x=cur.loc/3;
        int y=cur.loc%3;

        for(int i=0;i<4;i++)
        {
            int tx=x+move[i][0];
            int ty=y+move[i][1];

            if(tx<0||tx>2||ty<0||ty>2)continue;

            next=cur;
            next.loc=tx*3+ty;
            next.s[cur.loc]=next.s[next.loc];//换位
            next.s[next.loc]=0;//换位
            next.status=cantor(next.s);

            if(!vis[next.status])//记录路径
            {
                vis[next.status]=true;
                next.path=next.path+indexs[i];

                if(next.status==aim)
                {
                    path=next.path;
                    return true;
                }

                q.push(next);
            }
        }
    }
    return false;
}

int main()
{
    char ch;
    while(cin>>ch)
    {
        if(ch=='x')
        {
            ncur.s[0]=0;
            ncur.loc=0;
        }
        else
            ncur.s[0]=ch-'0';

        for(int i=1;i<9;i++)
        {
            cin>>ch;
            if(ch=='x')
            {
                ncur.s[i]=0;
                ncur.loc=i;
            }
            else ncur.s[i]=ch-'0';
        }
        
        ncur.status=cantor(ncur.s);
        
        if(bfs())
        {
            cout<<path<<endl;
        }
        else cout<<"unsolvable"<<endl;
    }
    return 0;
}
BFS

 

二、DBFS+康托展开+回溯保存路径
 
代码如下:0ms
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;

const int MAXN = 400000;

int has[MAXN];
int fac[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
int last1[MAXN], last2[MAXN];
int mov1[MAXN], mov2[MAXN];

struct State {
    int s[9];
    int pos;
    int hash;
} ini, target;//target是目标状态
queue<State> q1, q2;

int Hash(int* s)//康托展开
{
    int res = 0;
    for(int i = 0; i < 8; i++)
    {
        int cnt = 0;
        for(int j = i + 1; j < 9; j++)
        {
            if(s[i] > s[j]) cnt++;
        }
        res += cnt * fac[8-i];
    }
    return res;
}

int cal_pos(int pos, int i)//计算0的位置
{
    int nx = pos/3+dx[i];
    int ny = pos%3+dy[i];
    if(nx<0 || nx>2 || ny<0 || ny>2)
        return -1;
    return nx*3 + ny;
}

void DBFS_init()
{
    while(!q1.empty()) q1.pop();
    while(!q2.empty()) q2.pop();

    for(int i = 0; i < 9; i++)
        target.s[i] = i+1;
    target.pos = 8;
    target.hash = 0;

    has[0] = 2;
    q2.push(target);
    q1.push(ini);
    has[ini.hash] = 1;
}

int q1_expand()
{
    if(q1.empty())
        return -1;

    State ha = q1.front();
    q1.pop();

    State tmp;

    for(int i = 0; i < 4; i++)
    {
        tmp.pos = cal_pos(ha.pos, i);

        if(tmp.pos<0) continue;

        for(int j = 0; j < 9; j++)
        {
            if(j==ha.pos)
                tmp.s[j] = ha.s[tmp.pos];
            else if(j == tmp.pos)
                tmp.s[j] = ha.s[ha.pos];
            else
                tmp.s[j] = ha.s[j];
        }
        tmp.hash = Hash(tmp.s);

        if(has[tmp.hash] == 1) continue;

        q1.push(tmp);
        last1[tmp.hash] = ha.hash;
        mov1[tmp.hash] = i;

        if(has[tmp.hash] == 2)
            return tmp.hash;
        has[tmp.hash] = 1;
    }
    return -1;
}

int q2_expand()
{
    if(q2.empty())
        return -1;

    State ha = q2.front();
    q2.pop();

    State tmp;

    for(int i = 0; i < 4; i++)
    {
        tmp.pos = cal_pos(ha.pos, i);
        if(tmp.pos<0) continue;

        for(int j = 0; j < 9; j++)
        {
            if(j==ha.pos)
                tmp.s[j] = ha.s[tmp.pos];
            else if(j == tmp.pos)
                tmp.s[j] = ha.s[ha.pos];
            else
                tmp.s[j] = ha.s[j];
        }
        tmp.hash = Hash(tmp.s);

        if(has[tmp.hash] == 2) continue;

        q2.push(tmp);
        last2[tmp.hash] = ha.hash;
        mov2[tmp.hash] = i;

        if(has[tmp.hash] == 1)
            return tmp.hash;
        has[tmp.hash] = 2;
    }
    return -1;
}

int DBFS()
 {
    DBFS_init();
    while(!q1.empty() || !q2.empty())
    {
        int ans1 = q1_expand();
        if(ans1 >= 0)
            return ans1;

        int ans2 = q2_expand();
        if(ans2 >= 0)
            return ans2;
    }
    return -1;
}

void print_path1(int x)
{
    if(x==ini.hash) return;

    print_path1(last1[x]);
    int i = mov1[x];
    if(!i)
        printf("u");
    else if(i==1)
        printf("l");
    else if(i==2)
        printf("d");
    else
        printf("r");
}

void print_path2(int x)
{
    if(x==0) return;

    int i = mov2[x];
    if(!i)
        printf("d");
    else if(i==1)
        printf("r");
    else if(i==2)
        printf("u");
    else
        printf("l");
    print_path2(last2[x]);
}

int main()
{
    char tmp;
    while(cin >> tmp)
    {
        memset(has, 0, sizeof(has));

        if(tmp != 'x')
            ini.s[0] = tmp - '0';
        else
        {
            ini.s[0] = 9;
            ini.pos = 0;
        }
        for(int i = 1; i < 9; i++)
        {
            cin >> tmp;
            if(tmp == 'x')
            {
                ini.s[i] = 9;
                ini.pos = i;
            }
            else
                ini.s[i] = tmp - '0';
        }

        ini.hash = Hash(ini.s);
        int ans = DBFS();

        if(ans < 0)
            printf("unsolvable");
        else
        {
            print_path1(ans);
            print_path2(ans);
        }
        puts("");
    }
    return 0;
}
DBFS

 

 
三、A*+曼哈顿距离
 
利用优先队列,重载结构体运算符
代码如下:16ms
 
#include <iostream>
#include <queue>
#include <string.h>
#include<cmath>
#include<stdio.h>
using namespace std;

const int maxN=500000;
const int aim=46233;

int fac[]={1,1,2,6,24,120,720,5040,40320};
int vis[maxN],g[maxN];
int dirx[]={0,0,-1,1};
int diry[]={-1,1,0,0};
int goal_x[]={0,0,0,1,1,1,2,2};
int goal_y[]={0,1,2,0,1,2,0,1};
char word[8]="lrud";

struct NODE
{
    int s[9];
    int loc; //location of '0'
    int status;//cantor value
    int f;//f=g+h
    string path;
    bool operator <(const NODE &a) const{
        return f>a.f;
    }
};
priority_queue<NODE> open;
string path;

int cantor(int s[])
{
    int sum=0;
    for(int i=0;i<9;++i)
    {
        int cnt=0;
        for(int j=i+1;j<9;++j)
            if(s[i]>s[j])
                ++cnt;
        sum+=cnt*fac[9-i-1];
    }
    return sum;
}

//该点与目标函位置的哈密顿距离
int h(int s[])
{
    int sum=0;
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j)
        {
            int k=i*3+j;
            if(s[k]!=9)
                sum+=fabs(i-goal_x[s[k]-1])+fabs(j-goal_y[s[k]-1]);
        }
    return sum;
}

int a_star(NODE start)
{
    memset(vis,0,sizeof(vis));
    while(!open.empty()) open.pop();

    NODE cur,nxt;
    open.push(start);
    vis[start.status]=1;
    while(!open.empty())
    {
        cur=open.top();
        open.pop();
        if(cur.status==aim)
        {
            path=cur.path;
            return 1;
        }
        int x=cur.loc/3;
        int y=cur.loc%3;
        for(int i=0;i<4;++i)
        {
            int tx=x+dirx[i];
            int ty=y+diry[i];
            if(tx<0||tx>=3||ty<0||ty>=3)
                continue;

            nxt=cur;
            nxt.loc=tx*3+ty;
            nxt.s[cur.loc]=nxt.s[nxt.loc];
            nxt.s[nxt.loc]=0;
            nxt.status=cantor(nxt.s);
            if(vis[nxt.status]==0)//0:not visited 1:in colse or open
            {
                vis[nxt.status]=1;
                g[nxt.status]=g[cur.status]+1;
                nxt.f=g[nxt.status]+3*h(nxt.s);//h>h* can not always found the best solution,but can find a solution
                nxt.path=nxt.path+word[i];

                open.push(nxt);
            }
        }
        vis[cur.status]=1;
    }
    return 0;
}

int main()
{
    char tmp[4];
    NODE ncur;

    for(int i=0;i<9;++i)
    {
        scanf("%s",tmp);
        if(tmp[0]=='x')
        {
            ncur.s[i]=0;
            ncur.loc=i;
        }
        else
            ncur.s[i]=tmp[0]-'0';
    }

    ncur.status=cantor(ncur.s);
    g[ncur.status]=0;
    ncur.f=h(ncur.s);

    if(a_star(ncur))
        cout<<path;
    else
        puts("unsolvable");
    return 0;
}
A*

 

 
四、IDA*
 
代码如下:0ms
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cmath>

#define MAXN 105
#define INF 1000000000

using namespace std;

struct wwj
{
    int pos;
    char x[12];//当前状态
}tmp;

int top, found, st[400000], mi;
int d[] = {-1, -3, 1, 3};//0位置编号的变化
char as[] = "lurd";
bool move[][4] = {{0,0,1,1}, {1,0,1,1}, {1,0,0,1}, {0,1,1,1}, {1,1,1,1}, {1,1,0,1}, {0,1,1,0}, {1,1,1,0}, {1,1,0,0}};

//估价函数:计算曼哈顿距离
int h(wwj &t)
{
    int sum = 0;
    for(int i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
        {
            int k = i * 3 + j;
            if(t.x[k] == 'x') continue;
            sum += abs(i - (t.x[k] - '1') / 3) + abs(j - (t.x[k] - '1') % 3);
        }
    return sum;
}

void output()
{
    for(int i = 1;i <= top; i++) 
        printf("%c", as[st[i]]);
    printf("\n");
}

//p:当前0的位置;dp:当前深度;maxdp:限制深度
void dfs(int p, int dp, int maxdp)
{
    int f = h(tmp);//当前状态的估价函数
    int i, t;
    
    if(mi > f) 
        mi = f;
    if(f + dp > maxdp || found) 
        return;
    
    if(f == 0)//到达目标状态
    {
        output();
        found = 1;
        return;
    }
    
    for(i = 0;i < 4; i++)
    {
        if(move[p][i] == 0) continue;
        
        t = d[i] + p;//t是0的位置
        
        //一个重要的剪枝,如果之前的一步和现在的一步是相反的,无疑是做了无用功,不必搜索。
        //即:状态判重
        if(top > 0 && d[st[top]] + d[i] == 0) continue;
        
        swap(tmp.x[t], tmp.x[p]);
        st[++top] = i;
        
        dfs(t, dp + 1, maxdp);
        
        top--;
        swap(tmp.x[t], tmp.x[p]);//交换位置
    }
}

void IDA()
{
    int maxdp = h(tmp);//初始为初状态的h值
    found = 0;
    
    while(found == 0)
    {
        mi = 0x7fffffff; //mi表示在当前的h值限制下所能搜到的最优位置,即h值最小的位置
        top = 0;
        
        dfs(tmp.pos, 0, maxdp);
        
        maxdp += mi; //如果不能搜到就将mi加上去
    }
}

int main()
{
    for(int i = 0; i < 9; i++)
    {
        scanf("%s", &tmp.x[i]);
        if(tmp.x[i] == 'x') 
            tmp.pos = i;
    }
    IDA();
    return 0;
}
IDA*

 

posted @ 2016-07-10 10:06  琥珀川||雨露晨曦  阅读(116)  评论(0)    收藏  举报