题解——八数码难题

思路

由明确的两种状态可以想到

D(double)BFS即双向BFS

输入的是个

283104765

然而 凡是搜索都有个标记即vis[]

但 按题意3 * 3的地图不好标记(也许用map可以)

于是直接用一维dir数组改成

int dir[4] = {1,-3,-1,3};

但九位数也是开不了的

就有一个cantor展开式

状态压缩一下

inline int cantor(int a[])
{
    int i,j,ans = 0;
    for(i = 0;i < 9;i++)
    {
        int s = 0;
        for(j = i + 1;j < 9;j++)
            if(a[j] < a[i]) s++;
        ans += s * fac[8 - i];
    }
    return ans;
}

然后就行了 DBFS直接套板子啊

优化

其实就上面的好像就可以过了

但还有个优化

逆序对有关

将3 * 3 的图 弄成序列

可以得出结论:初始状态的逆序对模2必须等于目标状态的逆序对模2,否则无法达成,输出-1

证明

可以转为 初始状态无论如何改变其奇偶性不变

位置图

1 2 3

4 5 6

7 8 9

将3 * 3 的图 弄成序列

1 2 3 4 5 6 7 8 9

注意 0 不算入队列 所以上面实际上是8个

~~

2 8 3

1 0 4

7 6 5

可见 当位置5的0与位置8的6交换会有几种情况

位置n的数 为方便简写为(n)

Case1:(6)和(7)中有0个比(8)小

则逆序对+ 2

Case2:(6)和(7)中有1个比(8)小

则逆序对- 1 + 1

Case2:(6)和(7)中有2个比(8)小

则逆序对+ 2

综上逆序对奇偶性不变

其它交换情况可以类似讨论

代码

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 400000;
int step[2][MAXN],fac[10] = {1,1,2,6,24,120,720,5040,40320,362880};
bool vis[2][MAXN];
int dir[4] = {1,-3,-1,3};

//cantor状态压缩
inline int cantor(int a[])
{
    int i,j,ans = 0;
    for(i = 0;i < 9;i++)
    {
        int s = 0;
        for(j = i + 1;j < 9;j++)
            if(a[j] < a[i]) s++;
        ans += s * fac[8 - i];
    }
    return ans;
}
struct Node
{
    int word[9],loc,ca;
    bool f;
    void node(int Word[],int LOC,int CA,bool F)
    {
        for(int i = 0;i < 9;i++) word[i] = Word[i];
        loc = LOC,ca = CA,f = F;
    }
}Start,order;
inline int dbfs()
{
    queue<Node> q;
    Start.f = 1,order.f = 0;
    q.push(Start);
    q.push(order);
    int i;
    while(!q.empty())
    {
        Node tmp = q.front();
        q.pop();
        if(vis[!tmp.f][tmp.ca])
            return (step[1][tmp.ca] + step[0][tmp.ca]);
        int Step = step[tmp.f][tmp.ca];
        for(i = 0;i < 4;i++)
        {
            int poss = tmp.loc + dir[i];
            
            //由于dir数组改了 判断也得改
            if(0 <= poss&&poss < 9&&(tmp.loc % 3 == poss % 3||tmp.loc / 3 == poss / 3))
            {
                swap(tmp.word[tmp.loc],tmp.word[poss]);
                int nca = cantor(tmp.word);
                if(!vis[tmp.f][nca])
                {
                    Node g;
                    vis[tmp.f][nca] = 1;
                    step[tmp.f][nca] = Step + 1;
                    g.node(tmp.word,poss,nca,tmp.f);
                    q.push(g);
                }
                swap(tmp.word[tmp.loc],tmp.word[poss]); 
            }
        }
    }
    return -1;
}

//优化 求逆序对
inline int cutdown(int a[])
{
    int i,j,q = 0;
    for(i = 0;i < 9;i++)
        for(j = i + 1;j < 9;j++)
            if(a[i] != 0&&a[j] != 0&&a[j] < a[i]) q++;
    return q;
}
int main()
{
    int i;
    for(i = 0;i < 9;i++) 
    {
        scanf("%d",&Start.word[i]);
        if(!Start.word[i]) Start.loc = i;
    }
    for(i = 0;i < 9;i++)
    {
        scanf("%d",&order.word[i]);
        if(!order.word[i]) order.loc = i;
    }
    //优化
    if(cutdown(Start.word) % 2 != cutdown(order.word) % 2)
    {
        printf("-1");
        return 0;
    }
    Start.ca = cantor(Start.word);
    vis[1][Start.ca] = 1;
    order.ca = cantor(order.word);
    vis[0][order.ca] = 1;
    printf("%d",dbfs());
    return 0;
}

转载于:https://www.cnblogs.com/resftlmuttmotw/p/11323219.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值