NOJ 1043 跳马

本文介绍了一个使用双向广度优先搜索解决国际象棋上马从一个位置跳到另一个位置最少步数的问题。通过维护两个队列,分别从起点和终点进行搜索,当两队列相遇时即得到最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2016.11.11

【题目描述】
1043.跳马

时限:1000ms 内存限制:10000K 总时限:3000ms

描述
现有一200*200大小的国际象棋棋盘,棋盘中仅有一个马,给定马的当前位置(S)和目标位置(T),求出马最少需要多少跳才能从当前位置到达目标位置。

输入
本题包含多个测例。输入数据的第一行有一个整数N(1<=N<=1000),表示测例的个数,接下来的每一行有四个以空格分隔的整数,分别表示马当前位置及目标位置的横、纵坐标C(x,y)和G(x,y)。坐标由1开始。
输出
对于每个测例,在单独的一行内输出一个整数,即马从当前位置跳到目标位置最少的跳数。

输入样例
2
1 1 2 1
1 5 5 1
输出样例
3
4

【解题思路】
双向广搜,维护两个队列,分别从起点和终点搜索,碰头的时候结束。
双向队列扩展的小技巧,优先扩展节点数较少的队列

【代码实现】

# include <stdio.h>
# include <queue>

using namespace std;

int dx[10] = {0, 1, 2, 2, 1, -1, -2, -2, -1};
int dy[10] = {0, -2, -1, 1, 2, 2, 1, -1, -2};
int used[3][205][205];
struct coordinate {
    int x, y;
}start, end;
queue<coordinate> q[3];

void init(void);
int bfs(int , int );
int can(int ,int );
coordinate move(coordinate , int );

int main(void)
{   
    int n, num, k;
    int a, b;

    scanf("%d", &n);

    while (n--)
    {
        k = 0;
        init();
        while(q[1].size())
        {
            if (q[1].size() < q[2].size())
                num = bfs(k, 1);
            else
                num = bfs(k, 2);
            if (num != -1)
                break;
        }
        printf("%d\n", num);
        while(q[1].size())
            q[1].pop();
        while(q[2].size())
            q[2].pop();
    }

    return 0;
}

int bfs(int k,int m)
{
    int i;
    coordinate t1,t2;

    t1 = q[m].front();
    q[m].pop();
    for (i = 1; i <= 8; ++i)
    {
        if (can(t1.x+dx[i],t1.y+dy[i]))
        {
            t2 = move(t1, i);
            if (used[3-m][t2.x][t2.y] != -1)
                return used[3-m][t2.x][t2.y] + used[m][t1.x][t1.y] + 1;
            if (used[m][t2.x][t2.y] == -1)
            {
                used[m][t2.x][t2.y] = used[m][t1.x][t1.y] + 1;
                q[m].push(t2);
            }
        }
    }
    return -1;
}

void init(void)
{
    int i, j;

    scanf("%d%d%d%d", &start.x, &start.y, &end.x, &end.y);
    for (i = 1; i <= 200; ++i)
        for (j = 1; j <= 200; ++j)
            used[1][i][j] = used[2][i][j] = -1;
    used[1][start.x][start.y] = 0;
    used[2][end.x][end.y] = 0;
    q[1].push(start);
    q[2].push(end);
}

int can(int x,int y)
{
    if (x <= 0 || x >= 201 || y <= 0 || y >= 201)
        return 0;
    return 1;
}

coordinate move(coordinate a, int k)
{
    coordinate b;

    b.x = a.x + dx[k];
    b.y = a.y + dy[k];

    return b;
}

【心得体会】
了解双向广搜的基本模式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值