[IDDFS][双向BFS] 骑士精神


题目描述

骑士精神

解题思路(IDDFS)

一看这个题,想不出什么神奇的方法,再想一下,直接搜索吧

搜索的顺序很显然,从空地开始,往八个方向进行搜索,如果某一次的操作能得到目标矩阵,就可以退出了

因为题目限制了最多 15 15 15步,就很明显是IDDFS了,但如果只是限制一个上限深度还不够,仍然会超时
此时我们就需要用到乐观估计函数了,每一道题的乐观估计函数是不同的,针对这道题,如果在当前状态下要还原到目标矩阵,最理想的状态就是每用一步就可以让这个棋子到位,所以我们只需要统计当前矩阵和目标矩阵不同的元素个数来作为乐观估计函数就可以了

参考代码(IDDFS)

#include <cstdio>
#include <cstring>

template <class T>
T read() {
    T x = 0; T f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
    return x * f;
}

template <typename T>
void wri(T x) {
    if(x < 0) {x = -x; putchar('-');}
    if(x / 10) wri(x / 10);
    putchar(x % 10 + 48);
}

template <typename T>
void write(T x, char s) {
    wri(x);
    putchar(s);
}

int n, sx, sy;
int dir[8][2] = {{1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};
char a[10][10], s[10][10] = {{'0', '0', '0', '0', '0', '0'}, {'0', '1', '1', '1', '1', '1'}, {'0', '0', '1', '1', '1', '1'}, {'0', '0', '0', '*', '1', '1'}, {'0', '0', '0', '0', '0', '1'}, {'0', '0', '0', '0', '0', '0'}};	//s为目标矩阵
bool f;

int check() {	//乐观估计函数
    int ret = 0;
    for(int i = 1;i <= 5;i ++)
        for(int j = 1;j <= 5;j ++)
            if(a[i][j] != s[i][j])
                ret ++;
    return ret;
}

bool inside(int x, int y) {
    if(x < 1 || x > 5 || y < 1 || y > 5) return 0;
    return 1;
}

void IDDFS(int nowDepth, int maxDepth, int x, int y) {
    if(nowDepth > maxDepth) return ;
    if(check() == 0) {f = 1; return ;}	//如果已经得到目标矩阵
    for(int i = 0;i <= 7;i ++) {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if(! inside(tx, ty)) continue;
        a[x][y] = a[tx][ty], a[tx][ty] = '*';
        if(nowDepth + check() <= maxDepth)	//如果当前的深度加上最乐观的情况都会超过限制深度则退出,否则进行下一层搜索
            IDDFS(nowDepth + 1, maxDepth, tx, ty);
        a[tx][ty] = a[x][y], a[x][y] = '*';
        if(f) return ;
    }
}

int main() {
    scanf("%d", &n);
    while(n --) {
        f = 0;
        for(int i = 1;i <= 5;i ++) {
            scanf("\n");
            for(int j = 1;j <= 5;j ++)
                scanf("%c", &a[i][j]);
        }
        for(int i = 1;i <= 5;i ++)
            for(int j = 1;j <= 5;j ++)
                if(a[i][j] == '*')
                    sx = i, sy = j;
        for(int i = 0;i <= 15;i ++) {
            IDDFS(0, i, sx, sy);
            if(f) {
                write(i, '\n');
                break;
            }
        }
        if(! f)
            puts("-1");
    }
    return 0;
}

解题思路(双向BFS)

正向起点为起始棋盘,反向起点为目标棋盘,这种方法的难点就是判重,如何确定当前状态是否被访问过

由于题目除空格外只有两种棋子,我们可以将其看作是一个二进制数,对每一个位置进行编号(除去空格的位置),然后算出其十进制的值
但这样是会出现重复情况的,例如这样两个棋盘(*号用2代替):
[ 12111 11111 11100 00000 00000 ] \begin{bmatrix} 12111 \\ 11111\\ 11100\\ 00000\\ 00000\end{bmatrix} 1211111111111000000000000 [ 11211 11111 11100 00000 00000 ] \begin{bmatrix}11211\\ 11111\\ 11100\\ 00000\\ 00000\end{bmatrix} 1121111111111000000000000因为我们编号是不算 2 2 2的,所以这样算出来的两个数就会相同,所以我们需要另外开一维数组来记录二的编号

参考代码(双向BFS)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;

template <class T>
T read() {
    T x = 0; T f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
    return x * f;
}

template <typename T>
void wri(T x) {
    if(x < 0) {x = -x; putchar('-');}
    if(x / 10) wri(x / 10);
    putchar(x % 10 + 48);
}

template <typename T>
void write(T x, char s) {
    wri(x);
    putchar(s);
}

template <typename T>
T Max(T x, T y) {return x > y ? x : y;}

template <typename T>
T Min(T x, T y) {return x < y ? x : y;}

template <typename T>
T Fabs(T x) {return x < 0 ? -x : x;}

template <typename T>
void Swap(T &x, T &y) {T t = x; x = y, y = t;}

struct node {
    int f, idxx, idxy;
    int p[10][10];
};
node start, goal;

queue <node> que;

map <int, bool> vis[2][30];
map <int, int> step[2][30];

int n;
int dir[8][2] = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
char s;

int qkpow(int x, int y) {
    int ret = 1;
    while(y) {
        if(y & 1)
            ret = ret * x;
        x = x * x;
        y >>= 1;
    }
    return ret;
}

int getIdx(node x) {	//求出空格的位置
    for(int i = 1;i <= 5;i ++)
        for(int j = 1;j <= 5;j ++)
            if(x.p[i][j] == 2)
                return (i - 1) * 5 + j;
    return 0;
}

int getHash(node x) {	//判重hash
    int ret = 0, sum = 0;
    for(int i = 1;i <= 5;i ++)
        for(int j = 1;j <= 5;j ++) {
            if(x.p[i][j] != 2) sum ++;
            if(x.p[i][j] == 1) ret += qkpow(2, sum);
        }
    return ret;
}

bool inside(int x, int y) {
    if(x < 1 || x > 5 || y < 1 || y > 5) return 0;
    return 1;
}

int DWBFS() {	//双向BFS
    que.push(start);
    que.push(goal);
    int idx1 = getIdx(start), idx2 = getIdx(goal);
    int sum1 = getHash(start), sum2 = getHash(goal);
    vis[start.f][idx1][sum1] = vis[goal.f][idx2][sum2] = 1;
    while(! que.empty()) {
        node t = que.front();
        que.pop();
        idx1 = getIdx(t);
        sum1 = getHash(t);
        if(vis[! t.f][idx1][sum1])
            return step[t.f][idx1][sum1] + step[! t.f][idx1][sum1] <= 15 ? step[t.f][idx1][sum1] + step[! t.f][idx1][sum1] : -1;
        for(int i = 0;i <= 7;i ++) {
            node tmp = t;
            tmp.idxx += dir[i][0];
            tmp.idxy += dir[i][1];
            if(! inside(tmp.idxx, tmp.idxy)) continue;
            Swap(tmp.p[tmp.idxx][tmp.idxy], tmp.p[t.idxx][t.idxy]);
            idx2 = getIdx(tmp);
            sum2 = getHash(tmp);
            if(! vis[tmp.f][idx2][sum2]) {
                vis[tmp.f][idx2][sum2] = 1;
                step[tmp.f][idx2][sum2] = step[t.f][idx1][sum1] + 1;
                if(step[tmp.f][idx2][sum2] < 8)
                    que.push(tmp);
            }
        }
    }
    return -1;
}

int main() {
    goal.p[1][1] = goal.p[1][2] = goal.p[1][3] = goal.p[1][4] = goal.p[1][5] = goal.p[2][2] = goal.p[2][3] = goal.p[2][4] = goal.p[2][5] = goal.p[3][4] = goal.p[3][5] = goal.p[4][5] = 1, goal.p[3][3] = 2;
    goal.f = 0, goal.idxx = 3, goal.idxy = 3;
    scanf("%d", &n);
    while(n --) {
        while(! que.empty()) que.pop();
        for(int i = 0;i <= 1;i ++)
            for(int j = 1;j <= 25;j ++)
                vis[i][j].clear(), step[i][j].clear();
        for(int i = 1;i <= 5;i ++) {
            s = getchar();
            for(int j = 1;j <= 5;j ++) {
                s = getchar();
                if(s == '*') {start.p[i][j] = 2, start.idxx = i, start.idxy = j;}
                else start.p[i][j] = s - 48;
            }
        }
        start.f = 1;
        write(DWBFS(), '\n');
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值