Eight (HDU - 1043|POJ - 1077)(A* | 双向bfs+康拓展开)

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.

InputYou will receive, several descriptions of configuration of the 8 puzzle. One 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
OutputYou 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. Do not print a blank line between cases.
Sample Input

2  3  4  1  5  x  7  6  8

Sample Output

ullddrurdllurdruldr

题意:给出8个数和一个空位(x表示),空位可以向四个方向和数交换,求最小的操作方式试其变成 (1 2 3 4 5 6 7 8 x)
思路:奇数码问题,先判断是否有解,因为是奇数码,所以有解的情况是两个情况的逆序对奇偶相同。
另外,可以把x置换成9,然后对这个9位数进行标记,可以用map<int,int>,也可以用康托展开将其和自然数一一对应。
A*的评估函数是每个位置到其应当位置的曼哈顿距离

A* + map + string路径记录:
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;

struct Node
{
    int cost,status;
    int s[10];
    int pos;
    int f;
    string ans;
    Node(int co=0,int st=0,int pos=0,int f=0,string ans=""):cost(co),status(st),pos(pos),f(f),ans(ans) {}
    bool operator <(const Node x)const
    {
        return cost > x.cost;
    }
};
map<int,int>mp;
int ways[4][2] = {1,0,-1,0,0,1,0,-1};
char WAYS[4] = {'d','u','r','l'};
int ending;
Node start;
string ans;
int Merge(int *a,int l,int r)
{
    int mid = (r+l)>>1;
    int i=l,j=mid+1;
    int b[r-l+5];
    int cnt = 0;
    int k = 0;
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j])
        {
            b[cnt++] = a[i++];
        }
        else
        {
            b[cnt++] = a[j++];
            k += mid - i + 1;
        }
    }
    while(i <= mid)
        b[cnt++] = a[i++];
    while(j <= r)
        b[cnt++] = a[j++];
    for(int i=0; i<cnt; i++)
    {
        a[l++] = b[i];
    }
    return k;
}

void MER(int *a,int l,int r,int &cnt)
{
    if(l >= r)
        return;
    int mid = (l+r)>>1;
    MER(a,l,mid,cnt);
    MER(a,mid+1,r,cnt);
    cnt += Merge(a,l,r);
}

int turn(int s[])
{
    int cnt = 0;
    for(int i=1; i<=9; i++)
    {
        cnt *= 10;
        cnt += s[i];
    }
    return cnt;
}

int init(char word[])
{
    int a[10];
    for(int i=2; i<=9; i++)
        scanf(" %c",&word[i]);
    int cnt = 0;
    for(int i=1; i<=9; i++)
        if((word[i]) != 'x')
            a[++cnt] = word[i]-'0';
    int num = 0;
    MER(a,1,cnt,num);
    ending = 0;
    for(int i=1; i<=9; i++)
    {
        ending *= 10;
        ending += i;
        if(word[i] == 'x')
            start.s[i] = 9,start.pos = i;
        else
            start.s[i] = word[i] - '0';
    }
    return (num & 1) == 1;
}

int cal(int s[])
{
    int cnt = 0;
    for(int i=1; i<=9; i++)
    {
        if(s[i] == 9)
            continue;
        int x=(i-1)/3+1,y=(i-1)%3+1;
        int xx=(s[i]-1)/3+1,yy=(s[i]-1)%3+1;
        cnt += abs(x-xx)+abs(y-yy);
    }
    return cnt;
}

bool A_bfs(Node start)
{
    priority_queue<Node>que;
    while(!que.empty())
        que.pop();
    start.status = turn(start.s);
    start.f = cal(start.s);
    start.cost  = start.f;
    que.push(start);
    while(!que.empty())
    {
        Node tmp = que.top();
        que.pop();
        int status = tmp.status;
        int cost = tmp.cost - tmp.f;
        if(mp[status])
            continue;
        mp[status] = 1;
        if(status == ending)
        {
            ans = tmp.ans;
            return 1;
        };
        int x = (tmp.pos-1)/3+1;
        int y = (tmp.pos-1)%3+1;
        for(int i=0; i<4; i++)
        {
            int xx = x+ways[i][0];
            int yy = y+ways[i][1];
            if(xx < 1 || xx > 3 || yy < 1 || yy > 3)
                continue;
            Node t = tmp;
            t.s[(x-1)*3+y] = tmp.s[(xx-1)*3+yy];
            t.s[(xx-1)*3+yy] = 9;
            t.status = turn(t.s);
            t.f = cal(t.s);
            t.cost = cost+1+t.f;
            t.pos = (xx-1)*3+yy;
            t.ans += WAYS[i];
            que.push(t);
        }

    }
    return 0;
}

int main()
{
    char word[10];
    while(~scanf(" %c",&word[1]))
    {
        mp.clear();
        if(init(word))
            printf("unsolvable\n");
        else
        {
            A_bfs(start);
            cout << ans << endl;
        }
    }
}
View Code

双向bfs+康拓展开+递归路径记录(注:路径记录不能都用递归,会ME,也许姿势不对,对于从终态搜索的,因为其本身就是倒序,可以采用递推,从当前状态推过去)

#include<bits/stdc++.h>
using namespace std;

struct Node
{
    int cost,status;
    int s[10];
    int pos;
    Node(int co=0,int st=0,int pos=0):cost(co),status(st),pos(pos){}
};
const int maxn = 4e5;
int fac[9] = {1,1,2,6,24,120,720,5040,40320};
int ways[4][2] = {1,0,-1,0,0,1,0,-1};
char WAYS[4] = {'d','u','r','l'};
char FWAYS[4]= {'u','d','l','r'};
char path[maxn],path2[maxn];
int pre[maxn],pre2[maxn];
bool vis[2][maxn];
int ans;
queue<Node>que[2];
Node start,ending;
char word[10];
 int a[10],b[10];

int cantor(int s[])
{
    int sum = 0;
    for(int i=1; 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];
    }
    return sum+1;
}

int Merge(int *a,int l,int r)
{
    int mid = (r+l)>>1;
    int i=l,j=mid+1;
    int cnt = 0;
    int k = 0;
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j])
        {
            b[cnt++] = a[i++];
        }
        else
        {
            b[cnt++] = a[j++];
            k += mid - i + 1;
        }
    }
    while(i <= mid)
        b[cnt++] = a[i++];
    while(j <= r)
        b[cnt++] = a[j++];
    for(int i=0; i<cnt; i++)
    {
        a[l++] = b[i];
    }
    return k;
}

void MER(int *a,int l,int r,int &cnt)
{
    if(l >= r)
        return;
    int mid = (l+r)>>1;
    MER(a,l,mid,cnt);
    MER(a,mid+1,r,cnt);
    cnt += Merge(a,l,r);
}

int init(char word[])
{
    memset(vis,0,sizeof(vis));
    for(int i=2; i<=9; i++)
        scanf(" %c",&word[i]);
    int cnt = 0;
    for(int i=1; i<=9; i++)
        if((word[i]) != 'x')
            a[++cnt] = word[i]-'0';
    int num = 0;
    MER(a,1,cnt,num);
    for(int i=1; i<=9; i++)
    {
        ending.s[i] = i;
        if(word[i] == 'x')
            start.s[i] = 9,start.pos = i;
        else
            start.s[i] = word[i] - '0';
    }
    start.status = cantor(start.s);
    ending.status = cantor(ending.s);
    ending.pos = 9;
    return (num & 1) == 1;
}

bool bfs(int w)
{
    int num = que[w].size();
    while(num--)
    {

        Node tmp = que[w].front();
        que[w].pop();
        int status = tmp.status;
        int cost = tmp.cost;
        if(vis[w^1][status])
        {
            ans = status;
            return 1;
        }
        int x = (tmp.pos-1)/3+1;
        int y = (tmp.pos-1)%3+1;
        for(int i=0; i<4; i++)
        {
            int xx = x+ways[i][0];
            int yy = y+ways[i][1];
            if(xx < 1 || xx > 3 || yy < 1 || yy > 3 )
                continue;
            Node t = tmp;
            t.s[(x-1)*3+y] = tmp.s[(xx-1)*3+yy];
            t.s[(xx-1)*3+yy] = 9;
            t.status = cantor(t.s);
            if(vis[w][t.status])continue;
            vis[w][t.status] = 1;
            t.s[(x-1)*3+y] = tmp.s[(xx-1)*3+yy];
            t.s[(xx-1)*3+yy] = 9;
            t.cost = cost+1;
            t.pos = (xx-1)*3+yy;
            if(w == 0)path[t.status] = WAYS[i],pre[t.status] = status;
            else path2[t.status] = FWAYS[i],pre2[t.status] = status;
            que[w].push(t);
        }
    }
    return 0;
}

void out(int w)
{
    if(pre[w] == -1)
        return;
    out(pre[w]);
    printf("%c",path[w]);
}
void out2(int w)
{
    while(pre2[w] != -1)
    {
        printf("%c",path2[w]);
        w = pre2[w];
    }
}
void solve()
{
    while(!que[0].empty())que[0].pop();
    while(!que[1].empty())que[1].pop();
    que[0].push(start);
    que[1].push(ending);
    pre[start.status] = pre2[ending.status] = -1;
    vis[0][start.status] = vis[1][ending.status] = 1;
    while(!que[0].empty() || !que[1].empty())
    {
        if(bfs(0))
        {
            out(ans);
            out2(ans);
            puts("");
            return;
        }
        if(bfs(1))
        {
            out(ans);
            out2(ans);
            puts("");
            return;
        }
    }
}

int main()
{
    while(~scanf(" %c",&word[1]))
    {
        if(init(word))
            printf("unsolvable\n");
        else
        {
            solve();
        }
    }
}
View Code



猜你喜欢

转载自www.cnblogs.com/iwannabe/p/10617092.html