[SCOI2005]骑士精神 题解

[SCOI2005]骑士精神 题解

题目地址:洛谷:【P2324】[SCOI2005]骑士精神 – 洛谷、BZOJ:Problem 1085. — [SCOI2005]骑士精神

题目描述

在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士,且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。
给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:
spirit scoi05 - [SCOI2005]骑士精神 题解
为了体现出骑士精神,他们必须以最少的步数完成任务。

输入输出格式

输入格式:
第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

输出格式:
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

输入输出样例

输入样例#1:

2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100

输出样例#1:

7
-1

题解

考虑DFS,然而状态非常多,是\mathrm{C}_{25}^{12} * 13 \approx 6.8 \times 10^7的。由于最大步数有限制,我们想到了迭代加深搜索,并且配合上A*剪枝,就可以不TLE了。注意转移状态的时候,不妨让空格来跳而不是遍历找哪个马可以往空格上跳。
A*的估价函数,可以考虑随便写一个当前状态和目标状态有几个位置不一样。反正让这个函数始终不超过最优步数就行。

代码

// Code by KSkun, 2018/5
#include <cstdio>

#include <algorithm>

const int MAXN = 200005;

const char goal[5][6] = {
    "11111",
    "01111",
    "00*11",
    "00001",
    "00000"
};

const int fix[2][8] = {
    {1, 1, -1, -1, 2, 2, -2, -2}, 
    {2, -2, 2, -2, 1, -1, 1, -1}
};

inline bool reach(const char s[5][6]) {
    for(int i = 0; i < 5; i++) {
        for(int j = 0; j < 5; j++) {
            if(s[i][j] != goal[i][j]) return false;
        }
    }
    return true;
}

inline int eval(const char s[5][6]) {
    int res = 0;
    for(int i = 0; i < 5; i++) {
        for(int j = 0; j < 5; j++) {
            if(s[i][j] != goal[i][j]) res++;
        }
    }
    return res;
}

bool flag = false;

inline void dfs(int step, int lim, char s[5][6], int x, int y) {
    if(reach(s)) {
        flag = true; return;
    }
    for(int i = 0; i < 8; i++) {
        int nx = x + fix[0][i], ny = y + fix[1][i];
        if(nx < 0 || nx >= 5 || ny < 0 || ny >= 5) continue;
        std::swap(s[x][y], s[nx][ny]);
        if(eval(s) + step <= lim) dfs(step + 1, lim, s, nx, ny);
        if(flag) return;
        std::swap(s[x][y], s[nx][ny]);
    }
}

int T, sx, sy;
char st[5][6];

int main() {
    scanf("%d", &T);
    while(T--) {
        for(int i = 0; i < 5; i++) {
            scanf("%s", st[i]);
            for(int j = 0; j < 5; j++) {
                if(st[i][j] == '*') {
                    sx = i; sy = j;
                }
            }
        }
        if(reach(st)) {
            puts("0");
            continue;
        }
        flag = false;
        for(int i = 1; i <= 15; i++) {
            dfs(0, i, st, sx, sy);
            if(flag) {
                printf("%d\n", i);
                break;
            }
        }
        if(!flag) puts("-1");
    }
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据