【算法实验三】--【分支限界法】--跳马

本文介绍了解决国际象棋中马从一个位置跳到另一个位置最少步数的经典算法问题。通过广搜算法和队列操作,文章详细解释了如何寻找从起点到终点的最短路径。

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

1043.跳马

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

描述

在国际象棋中,马的走法与中车象棋类似,即俗话说的“马走日”,下图所示即国际象棋中马(K)在一步能到达的格子(其中黑色的格子是能到达的位置)。

现有一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

解析:这是一个典型的分支限界法用广搜和队列来解决的问题。一般来说,回溯法用到的是深搜,所以不断嵌套的调用函数;分支限界法用到的是广搜,没有嵌套调用的说法,只有用队列进行操作。我以前一直存在一个误区,为什么回溯即深搜结束后不能用return返回结果并结束输出,而广搜就可以。大家看两个函数的差别就可以,dfs嵌套调用,return只是返回上一层函数,不会结束整个深搜,深搜是遍历了整个子集树,最后有另一个循环找出最优的结果,因此是void dfs(),而广搜是找到结果就输出,不用便利整个子集树,因此是int bfs(),后者是有返回值的,返回值就是目标值,而广搜就一定要用到栈和队列(在这点上c++的方便性远高于c语言),深搜就一定会用到嵌套的递归函数。这是二者的本质差别。

下面来说一下这道题。跟前面的题一样,采用广搜的思路,从当前节点开始先一次性找出它所有的直属子节点,看有没有附和条件的,若没有就入队列并找它兄弟节点的全部子节点,直到所有兄弟节点全部找完没找到入队列才又从下层节点找。因为相同一层节点的步数是相同的,而子节点比父节点的步数大一步,广搜就是找到最小的符合要求的步数。当然,一个相同的坐标可以是不同层的节点(因为它有不同的方法抵达),所以可以有不同的步数,我们要找到最小的步数,就要找到最早到达它的方法。

所以这种题就要用到队列来操作。记录当前队列首元素,然后将其出队列,用记录的当前队列首位元素遍历所有的下层节点,遍历过程中若有符合要求的,直接return就可以,若没有就一一入队列准备以后使用,直到找到目标值为止。

#include <iostream>
#include<string.h>
#include<queue>
using namespace std;
struct addr{
    queue<int>x;
    queue<int>y;
}addr;
int sx,sy,tx,ty;
int step[201][201],used[201][201];
int dirx[8]={1,2,2,1,-1,-2,-2,-1},diry[8]={2,1,-1,-2,2,1,-1,-2};
void init()
{
    memset(step,0,sizeof(step));
    memset(used,0,sizeof(used));
    used[sx][sy]=1;
    step[sx][sy]=0;
    addr.x.push(sx);
    addr.y.push(sy);
}
void empty()
{
    while(!addr.x.empty())
        addr.x.pop();
    while(!addr.y.empty())
        addr.y.pop();
}
int bfs()
{
    while(1)
    {
        int cx,cy;
        cx=addr.x.front();
        cy=addr.y.front();
        addr.x.pop();
        addr.y.pop();
        for(int i=0;i<8;i++)
        {
            int vx,vy;
            vx=cx+dirx[i];
            vy=cy+diry[i];
            if(vx==tx&&vy==ty)
                return step[cx][cy]+1;
            else
            {
                if(used[vx][vy]==0&&vx>0&&vx<=200&&vy>0&&vy<=200)
                {
                    addr.x.push(vx);
                    addr.y.push(vy);
                    step[vx][vy]=step[cx][cy]+1;
                    used[vx][vy]=1;
                }
            }
        }
    }

}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        cin>>sx>>sy>>tx>>ty;
        empty();
        init();
        int num=bfs();
        cout<<num<<endl;
    }
    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值