题目:

http://acm.hdu.edu.cn/showproblem.php?pid=1043
http://poj.org/problem?id=1077

题意:

经典8数码问题,求出任意一个操作过程

思路:

首先进行逆序数判断,可以发现,在八数码中,交换两个相连的位置,整个八数码中逆序数的数量的奇偶性不变,而终态的逆序数的奇偶性是偶,所以开始时检查初始状态逆序数的奇偶性,为奇的话则无解。然后求解操作过程,直接bfs的话,poj是可以过的,hdu是明显不能过的,但我们发现终点是唯一的,可以从终点出发进行一次逆向的bfs,把到达其他所有点的过程都记录下来,这样就是打表,以后直接查询即可,在hdu上效率是极快的,100ms左右。双向bfs直接搜的话,1000ms左右。用A*的话,效率跟估价函数的设计有关,定义g(n)为已走过的步数,h(n)为每个位置上的数字与终态相应位置上的数字的曼哈顿距离,f(n)=g(n)+h(n),直接用f(n)的话,效率1000ms左右,先h(n)在g(n)的话,效率500ms左右
逆向bfs:

//109ms
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;

struct node
{
    int val, idx;
    int a[N];
};

int fact[N], pre[M];
char op[M], str[M];
bool vis[M];

int dir[] = {-3, 3, -1, 1};
char dirc[] = {'d', 'u', 'r', 'l'};
void fact_table(int n)
{
    fact[0] = 1;
    for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
    int str = 0;
    for(int i = 1; i <= n; i++)
    {
        int rnk = 0;
        for(int j = i+1; j <= n; j++)
            if(s[i] > s[j]) rnk++;
        str += rnk * fact[n-i];
    }
    return str;
}
bool check(int a[], int n)
{
    int tot = 0;
    for(int i = 1; i <= n; i++)
        for(int j = i+1; j <= n; j++)
            if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
    return !(tot&1);
}
void bfs(node s)
{
    queue<node> que;
    memset(vis, 0, sizeof vis);
    memset(pre, -1, sizeof pre);
    que.push(s), vis[s.val] = true;
    while(! que.empty())
    {
        node p = que.front(); que.pop();
        for(int i = 0; i < 4; i++)
        {
            if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
            if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
            int x = p.idx + dir[i];
            if(x >= 1 && x <= 9)
            {
                swap(p.a[p.idx], p.a[x]);
                int val = Cantor_expansion(p.a, 9);
                if(! vis[val])
                {
                    vis[val] = true;
                    pre[val] = p.val;
                    op[val] = dirc[i];
                    swap(val, p.val);
                    swap(x, p.idx);
                    que.push(p);
                    swap(val, p.val);
                    swap(x, p.idx);
                }
                swap(p.a[p.idx], p.a[x]);
            }
        }
    }
}
int main()
{
    int a[N];
    fact_table(10);
    node p;
    p.idx = 9;
    for(int i = 1; i <= 9; i++) p.a[i] = i;
    p.val = Cantor_expansion(p.a, 9);
    bfs(p);
    while(gets(str))
    {
        int k = 0;
        for(int i = 0; str[i]; i++)
        {
            if(str[i] == 'x') a[++k] = 9;
            else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
        }
        if(! check(a, 9))
        {
            puts("unsolvable"); continue;
        }
        k = 0;
        int en = Cantor_expansion(a, 9);
        while(pre[en] != -1)
        {
            str[k++] = op[en];
            en = pre[en];
        }
        str[k] = '\0';
        puts(str);
    }
    return 0;
}

双向bfs:

//800ms
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;

struct node
{
    int val, idx;
    int a[N];
};

int fact[N], pre[M];
char op[M], str[M];
int vis[M];

int dir[] = {-3, 3, -1, 1};
char dirc[] = {'u', 'd', 'l', 'r'};
char dirr[] = {'d', 'u', 'r', 'l'};
void fact_table(int n)
{
    fact[0] = 1;
    for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
    int str = 0;
    for(int i = 1; i <= n; i++)
    {
        int rnk = 0;
        for(int j = i+1; j <= n; j++)
            if(s[i] > s[j]) rnk++;
        str += rnk * fact[n-i];
    }
    return str;
}
bool check(int a[], int n)
{
    int tot = 0;
    for(int i = 1; i <= n; i++)
        for(int j = i+1; j <= n; j++)
            if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
    return !(tot&1);
}
void bfs(node s, node t)
{
    queue<node> que;
    memset(vis, 0, sizeof vis);
    memset(pre, -1, sizeof pre);
    que.push(s), vis[s.val] = 1;
    que.push(t), vis[t.val] = 2;
    while(! que.empty())
    {
        node p = que.front(); que.pop();
        for(int i = 0; i < 4; i++)
        {
            if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
            if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
            int x = p.idx + dir[i];
            if(x >= 1 && x <= 9)
            {
                swap(p.a[p.idx], p.a[x]);
                int val = Cantor_expansion(p.a, 9);
                if(! vis[val])
                {
                    vis[val] = vis[p.val];
                    pre[val] = p.val;
                    op[val] = (vis[p.val] == 1) ?  dirc[i] : dirr[i];
                    swap(val, p.val);
                    swap(x, p.idx);
                    que.push(p);
                    swap(val, p.val);
                    swap(x, p.idx);
                }
                else if(vis[val] != vis[p.val])
                {
                    int k = 0;
                    if(vis[val] == 1)
                    {
                        int en = val;
                        while(pre[en] != -1) str[k++] = op[en], en = pre[en];
                        reverse(str, str + k);
                        str[k++] = (vis[p.val] == 1) ?  dirc[i] : dirr[i];
                        en = p.val;
                        while(pre[en] != -1) str[k++] = op[en], en = pre[en];
                        str[k++] = '\0';
                    }
                    else
                    {
                        int en = p.val;
                        while(pre[en] != -1) str[k++] = op[en], en = pre[en];
                        reverse(str, str + k);
                        str[k++] = (vis[p.val] == 1) ?  dirc[i] : dirr[i];
                        en = val;
                        while(pre[en] != -1) str[k++] = op[en], en = pre[en];
                        str[k++] = '\0';
                    }
                    return;
                }
                swap(p.a[p.idx], p.a[x]);
            }
        }
    }
}
int main()
{
    int a[N];
    fact_table(10);
    while(gets(str))
    {
        int k = 0, idx;
        for(int i = 0; str[i]; i++)
        {
            if(str[i] == 'x') a[++k] = 9, idx = k;
            else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
        }
        if(! check(a, 9))
        {
            puts("unsolvable"); continue;
        }
        node p, t;
        p.idx = idx;
        memcpy(p.a, a, sizeof a);
        p.val = Cantor_expansion(p.a, 9);

        t.idx = 9;
        for(int i = 1; i <= 9; i++) t.a[i] = i;
        t.val = Cantor_expansion(t.a, 9);
        bool flag = true;
        for(int i = 1; i <= 9; i++)
            if(p.a[i] != t.a[i]) flag = false;
        if(flag) puts("");//起点终点相同时,双向bfs无法求解,这里特判一下
        else
        {
            bfs(p, t);
            puts(str);
        }
    }
    return 0;
}

A∗搜索:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;

struct node
{
    int val, idx;
    int g, h;
    int a[N];
    friend bool operator< (node ta, node tb)
    {
        //return ta.g + ta.h > tb.g + tb.h; //1000ms
        if(ta.h != tb.h) return ta.h > tb.h;//500ms
        else return ta.g > tb.g;
    }
};

int fact[N], pre[M];
char op[M], str[M];
bool vis[M];

int dir[] = {-3, 3, -1, 1};
char dirc[] = {'u', 'd', 'l', 'r'};
void fact_table(int n)
{
    fact[0] = 1;
    for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
    int str = 0;
    for(int i = 1; i <= n; i++)
    {
        int rnk = 0;
        for(int j = i+1; j <= n; j++)
            if(s[i] > s[j]) rnk++;
        str += rnk * fact[n-i];
    }
    return str;
}
bool check(int a[], int n)
{
    int tot = 0;
    for(int i = 1; i <= n; i++)
        for(int j = i+1; j <= n; j++)
            if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
    return !(tot&1);
}
int get_h(node &p)
{
    int ans = 0;
    for(int i = 1; i <= 9; i++)
        ans += abs((i-1)/3 - (p.a[i]-1)/3) + abs((i-1)%3 - (p.a[i]-1)%3);
    return ans;
}
void a_star(node s, node t)
{
    priority_queue<node> que;
    memset(pre, -1, sizeof pre);
    memset(vis, 0, sizeof vis);
    s.g = 0, s.h = get_h(s);
    que.push(s), vis[s.val] = true;
    node p, tp;
    while(! que.empty())
    {
        p = que.top(); que.pop();
        if(p.val == t.val) return;
        for(int i = 0; i < 4; i++)
        {
            if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
            if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
            int x = p.idx + dir[i];
            if(x >= 1 && x <= 9)
            {
                tp = p;
                swap(tp.a[x], tp.a[tp.idx]);
                int val = Cantor_expansion(tp.a, 9);
                if(! vis[val])
                {
                    vis[val] = true;
                    pre[val] = p.val;
                    op[val] = dirc[i];
                    tp.idx = x, tp.val = val;
                    tp.h = get_h(tp), tp.g++;
                    que.push(tp);
                }
            }
        }
    }
}
int main()
{
    int a[N];
    fact_table(10);
    node t;
    t.idx = 9;
    for(int i = 1; i <= 9; i++) t.a[i] = i;
    t.val = Cantor_expansion(t.a, 9);
    while(gets(str))
    {
        int k = 0, idx;
        for(int i = 0; str[i]; i++)
        {
            if(str[i] == 'x') a[++k] = 9, idx = k;
            else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
        }
        if(! check(a, 9))
        {
            puts("unsolvable"); continue;
        }
        node p;
        p.idx = idx;
        memcpy(p.a, a, sizeof a);
        p.val = Cantor_expansion(p.a, 9);
        a_star(p, t);
        int en = t.val;
        k = 0;
        while(pre[en] != -1) str[k++] = op[en], en = pre[en];
        reverse(str, str + k);
        str[k++] = '\0';
        puts(str);
    }
    return 0;
}