2007年中山大学内部选拔赛第六试----九数码

本题的题意是:给出一个包含数字1~9的排列的目标状态和一系列不同的操作,再给出一些起始状态,求出从起始状态达到目标状态所需要的最少操作数。

首先这是一个九宫格,其实也就是求一个排列数,9!是所有的状态,即362880。

而每一个排列 都对应着一个康托展开数,开辟一个ans数组,下标是康托展开数,存入从目标状态到起始状态所需要的最小步数即可。

等到使用的时候直接进行查询,这样可以应对有很多输入的情况。

为什么说是从目标状态到起始状态呢?因为从起始状态到目标状态和从目标状态到起始状态的最小步数是一样的啊。

然后从目标状态123456789开始向所有状态广搜就可以了,每一次进入队列的对应状态都可以求出康托展开数,然后顺便通过now.step=pre.step+1求出步数即可。

对于康拓展开,百度百科,那里面说的很清楚,我还趁此机会顺便修改了词条,开心。

#include <stdio.h>
#include <string.h> 

const int MAXNODE = 362880;

struct State {
    char d[9];
    short f;
};

int pow[] = {1, 2, 6, 24, 120, 720, 5040, 40320};
int rot[4][9] = {{1, 4, 2, 0, 3, 5, 6, 7, 8}, {0, 2, 5, 3, 1, 4, 6, 7, 8}, {0, 1, 2, 4, 7, 5, 3, 6, 8}, {0, 1, 2, 3, 5, 8, 6, 4, 7}};
short ans[MAXNODE];
int head, tail;
State Q[MAXNODE];

int State2I(State &p)
{
    int ret = 0;
    for (int i = 0; i < 8;i++) {
        for (int j = i + 1; j < 9;j++) {
            if (p.d[i]>p.d[j])
            ret+=pow[7-i];
        }
    }
    return ret;
}

void PreCom()
{
    memset(ans, 255, sizeof(ans));

    head = -1;
    tail = 0;
    Q[0].f = 0;
    for (int i = 0; i < 9;i++)
        Q[0].d[i] = i + 1;
    ans[State2I(Q[0])] = 0;
    while (head++<tail) {
        State &p = Q[head], q;
        q.f = p.f + 1;
        for (int i = 0; i < 4;i++) {
            for (int j = 0; j < 9;j++) {
                q.d[j] = p.d[rot[i][j]];
            }
            int u = State2I(q);
            if (ans[u]<0) {
                ans[u] = q.f;
                Q[++tail] = q;
            }
        }
    }
}

void work()
{
    State p;
    int x;
    while (scanf("%d",&x)==1) {
        p.d[0] = x;
        for (int i = 1; i < 9;i++) {
            scanf("%d",&x);
            p.d[i] = x;
        }
        printf("%d\n", ans[State2I(p)]);
    }
}

int main()
{
    PreCom();
    work();
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值