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;
}

 

posted @ 2018-10-14 11:19  xyee  阅读(135)  评论(0编辑  收藏  举报