最少步数(广搜,剪枝)

最少步数(广搜,剪枝)

描述

在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”。有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字。

他的同桌平时喜欢下围棋,知道这件事后觉得很有趣,就想试一试,在一个(100×100)的围棋盘上任选两点A、B,A点放上黑子,B点放上白子,代表两匹马。棋子可以按“日”字走,也可以按“田”字走,俩人一个走黑马,一个走白马。谁用最少的步数走到左上角坐标为(1,1)的点时,谁获胜。现在他请你帮忙,给你A、B两点的坐标,想知道两个位置到(1,1)点可能的最少步数。

格式

输入格式
A、B两点的坐标。
输出格式
最少步数。

样例

输入样例
12 16
18 10
输出样例
8
9

代码:

#include <iostream>
#include <stdio.h>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int num[][2]={{-2,-2},{-2,-1},{-1,-2},{-2,1},{-2,2},{-1,2},{1,-2},{2,-2},{2,-1},{1,2},{2,1},{2,2}};
bool visit[128][128];
void init()
{
    for(int i=0;i<128;++i)
    {
        for(int j=0;j<128;++j)
        {
            visit[i][j]=false;
        }
    }
}
struct ver
{
    int row;//行
    int col;//列
    int step;//步数
};
void BFS(struct ver v)
{
    v.step=0;
    queue<ver> Myqueue;
    visit[v.row][v.col]=true;
    Myqueue.push(v);
    if(v.row==1&&v.col==1)
    {
        printf("%d\n", v.step);
        return ;
    }
    if(v.row<0||v.col<0||v.row>100||v.col>100)
    {
        return ;
    }
    while(!Myqueue.empty())
    {
        struct ver now=Myqueue.front();
        struct ver temp=now;
        Myqueue.pop();
        for(int i=0;i<12;++i)
        {
            int time=now.step;
            temp.row=now.row+num[i][0];
            temp.col=now.col+num[i][1];
            if(temp.row<0||temp.col<0||temp.row>100||temp.col>100)  continue;//防止产生非法数据
            if(visit[temp.row][temp.col])   continue;
            temp.step=time+1;
            visit[temp.row][temp.col]=true;
            if(temp.row==1&&temp.col==1)
            {
                printf("%d\n",temp.step);
                return ;
            }
            Myqueue.push(temp);
        }
    }
}
int main()
{
    struct ver v[2];
    for(int i=0;i<2;++i)
    {
        scanf("%d %d",&(v[i].row),&(v[i].col));
    }
    BFS(v[0]);
    init();
    BFS(v[1]);
    return 0;
}

分析

image

从左图可以看出,灰色的点是原点,然后周边红色的点是可以到达的点,设原点为(x,y),那么其他点就可以根据原点坐标推算出来,比如左下那个点的坐标为(x-2,y-2),一次推算下来我们可以得到一个n数组int num[][2]={{-2,-2},{-2,-1},{-1,-2},{-2,1},{-2,2},{-1,2},{1,-2},{2,-2},{2,-1},{1,2},{2,1},{2,2}};

然后进一步分析:我们可以发现有如下走法:

image

现在的问题是我们是一层一层的走呢,还是一次走到底再回来走另一条路,显然如果我们一条路走到底的方式更耗时,并且极有可能因为这条路走不通而陷入死循环,所以我们选择一层层。于是就有了以上代码。

代码中,我们首先要做的是判断数据是否已经符合,如果是,那就直接输出结果并返回,然后加上违规数据判断,判断不违规然后将该数据入队,层次遍历与队数据结构相同,故课采用队列的方式做广度优先搜索。当队列不为空的情况下循环遍历,每次取出一个数据,循环把这个数据接下来的12中走法一次入队,中间加上判断防止产生非法数据,和给已经到过的坐标打上标记,防止下次走老路(这里也是一个很重要的剪枝过程)。在后面我们直接判断本次要压入的情况是不是满足要求,是则输出数据直接返回,不是就入队,这里也减少了很多不必要的操作

总结模板:

1.判断边界条件,现在的数据是否已经符合要求

2.判断是否为非法数据

3.入队,并循环不为空

4.判断是否违法

5.判断是否标记,如果没有则标记

6.判断是否所求结果,是则返回,否则入队

posted @ 2021-04-10 21:53  5-StarrySky  阅读(228)  评论(0)    收藏  举报