hdu 2531 Catch him【BFS】

本文介绍了一种解决美式足球中擒杀四分卫问题的算法。通过BFS搜索策略,结合相对位置的概念,实现了防守球员在规避进攻锋线阻挡下成功擒杀四分卫的目标。

Catch him

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 771    Accepted Submission(s): 349


Problem Description
在美式足球中,四分卫负责指挥整只球队的进攻战术和跑位,以及给接球员传球的任务。四分卫是一只球队进攻组最重要的球员,而且一般身体都相对比较弱小,所以通常球队会安排5-7名大汉来保护他,其中站在四分卫前方、排成一线的5名球员称为进攻锋线,他们通常都是135公斤左右的壮汉。

对防守方来说,攻击对手的四分卫当然是最直接的限制对手进攻的方法。如果效果好,就可以在对方四分卫传球之前将其按翻在地,称之为擒杀。擒杀是最好的鼓舞防守队士气的方法,因为对方连传球的机会都没有,进攻就结束了,还必须倒退一些距离开球。凶狠的擒杀甚至能够将对方的四分卫弄伤,从而迫使对方更换这个进攻核心。
在本题中,输入给出准备擒杀四分卫的防守球员的位置、对方每个进攻锋线球员的位置以及对方四分卫的位置,你的任务是求出这名准备擒杀的防守球员至少要移动多少步,才能够擒杀对方四分卫。
假设对方进攻锋线和四分卫在这个过程中都不会移动。只有1名防守球员,防守球员只要碰到对方四分卫就算擒杀。
所有的球员都是一块连续的、不中空的2维区域。防守球员不可以从进攻锋线的身体上穿过,也不可以从界外穿过(只能走空地)。
防守队员不可以转动身体,只能平移。防守队员的身体所有部分向同一个方向(上、下、左、右)移动1格的过程叫做1步。
 

Input
输入包含多组数据。每组数据第一行都是两个整数H,W(0<H,W<=100),表示整个区域的高度和宽度,H=W=0表示输入结束。接下来有H行,每行W个字符。每个字符如果是’.’,表示这里是空地,如果是’O’,表示是进攻锋线队员的身体,如果是’D’,表示是准备擒杀的防守球员的身体,如果是’Q’,表示是四分卫的身体。
输入保证符合上面的条件。防守球员的身体总共不超过20格。

 

Output
对每组数据,输出包含擒杀所需最少步数的一行。如果不能擒杀,输出带’Impossible’的一行。
Sample Input
6 6
.Q....
QQ..OO
.OO..O
...O.O
OO.O..
....DD
7 7
.Q.....
QQ.OOO.
...O...
O......
OO..OO.
.O.....
.....DD
0 0
 


Sample Output
Impossible
9
 

中文题意,图搜。

解题过程+大体思路:

首先我们移动的是一个由好多格子或者单个格子组成的一个D(防守队员)。如果我们每一个点都进行移动的话未免有点太尴尬,而且vis【】【】判断是否走过该点的判断也不是很好写,这个时候我们需要一个抽象的想法:我们把这一坨D的最先出现的D(两层for遍历的情况下)当做一个头,我们移动头即可,那么我们怎样细节上处理这个题目呢?

如果我们有了一个标尺:“头” 之后呢,我们vis【】【】数组也就好处理了,对于头走过的点,标记上即可,那么他的身子要怎样移动呢?

我们都学过一个名词叫做:“相对”,关系可以相对,力可以相对,那么位子也可以相对。

对于这样的一个图:

ODO

ODO

ODD

我们规定第一个出现的D也就是坐标为(0,1)的点作为这个一坨D的头,我们可以通过头这样找到其他的身体部分:

(0,1)+(1,0)=(1,1)找到第二个出现的D,(0,1)+(2,0)=(2,1)找到第三个出现的D,(0,1)+(2,1)=(2,2)找到最后一个D。

那么我们可以通过保存(1,0)(2,0)(2,1)这三个x,y的相对位子值来通过“头”找到他们的身子。

这个时候小问题处理完毕了,我们就可以确定答题思路了:

用头漫无目的的BFS,每一次找到一个能走的位子都判断一下身子能否也可以达到相对位子,当然我们的“头”找到了Q的时候不要直接输出步数,我们还要判断他的身子能否走到合法的位子。同理,如果“身子”找到了Q的时候也不要直接输出步数,我们要确定所有身子的组成都能走到合法的位子才行,这个时候我们就不难写出代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
struct zuobiao
{
    int x,y,step;
}now,nex;
int n,m;
int cont;
int fang[25][2];
int vis[120][120];
char a[120][120];
int fx[4]={0,0,1,-1};
int fy[4]={-1,1,0,0};
void BFS(int x,int y)
{
    memset(vis,0,sizeof(vis));
    vis[x][y]=1;
    queue<zuobiao >s;
    now.x=x;
    now.y=y;
    now.step=0;
    s.push(now);
    while(!s.empty())
    {
        now=s.front();
        //printf("%d %d\n",now.x,now.y);
        s.pop();
        for(int i=0;i<4;i++)
        {
            nex.x=now.x+fx[i];
            nex.y=now.y+fy[i];
            if(nex.x>=0&&nex.x<n&&nex.y>=0&&nex.y<m&&vis[nex.x][nex.y]==0&&a[nex.x][nex.y]!='O')//左上角的头一定要合法才行
            {
                if(a[nex.x][nex.y]=='Q')//如果头遇到了Q
                {
                    int ok=1;
                    for(int j=0;j<cont;j++)//那么要判断其他身体部分都合法。
                    {
                        int xx=nex.x+fang[j][0];
                        int yy=nex.y+fang[j][1];
                        if(xx>=0&&xx<n&&yy>=0&&yy<m&&a[xx][yy]!='O')continue;
                        ok=0;
                    }
                    if(ok==1)
                    {
                        printf("%d\n",now.step+1);
                        return ;
                    }
                }
                int ok=1;
                int flag=0;
                for(int j=0;j<cont;j++)
                {
                    int xx=nex.x+fang[j][0];
                    int yy=nex.y+fang[j][1];
                    if(xx>=0&&xx<n&&yy>=0&&yy<m&&a[xx][yy]=='Q')//如果某个身子的部分碰到了Q,这个时候不要break,也要继续判断其他身子是否合法。
                    {
                        flag=1;
                    }
                    if(xx>=0&&xx<n&&yy>=0&&yy<m&&a[xx][yy]!='O')continue;
                    ok=0;
                }
                if(ok==1&&flag==1)
                {
                    printf("%d\n",now.step+1);
                    return ;
                }
                if(ok==1)
                {
                    vis[nex.x][nex.y]=1;
                    nex.step=now.step+1;
                    s.push(nex);
                }
            }
        }
    }
    printf("Impossible\n");
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0)break;
        cont=0;
        int sx,sy;
        for(int i=0;i<n;i++)
        {
            scanf("%s",a[i]);
            for(int j=0;j<m;j++)
            {
                if(a[i][j]=='D')
                {
                    if(cont==0)sx=i,sy=j,cont=1;//找到头
                    else
                    {
                        fang[cont][0]=i-sx;//记录身子的相对位子
                        fang[cont][1]=j-sy;
                        cont++;
                    }
                }
            }
        }
        //printf("%d\n",cont);
        BFS(sx,sy);
    }
}












下载前可以先看下教程 https://pan.quark.cn/s/16a53f4bd595 小天才电话手表刷机教程 — 基础篇 我们将为您简单的介绍小天才电话手表新机型的简单刷机以及玩法,如adb工具的使用,magisk的刷入等等。 我们会确保您看完此教程后能够对Android系统有一个最基本的认识,以及能够成功通过magisk root您的手表,并安装您需要的第三方软件。 ADB Android Debug Bridge,简称,在android developer的adb文档中是这么描述它的: 是一种多功能命令行工具,可让您与设备进行通信。 该命令有助于各种设备操作,例如安装和调试应用程序。 提供对 Unix shell 的访问,您可以使用它在设备上运行各种命令。 它是一个客户端-服务器程序。 这听起来有些难以理解,因为您也没有必要去理解它,如果您对本文中的任何关键名词产生疑惑或兴趣,您都可以在搜索引擎中去搜索它,当然,我们会对其进行简单的解释:是一款在命令行中运行的,用于对Android设备进行调试的工具,并拥有比一般用户以及程序更高的权限,所以,我们可以使用它对Android设备进行最基本的调试操作。 而在小天才电话手表上启用它,您只需要这么做: - 打开拨号盘; - 输入; - 点按打开adb调试选项。 其次是电脑上的Android SDK Platform-Tools的安装,此工具是 Android SDK 的组件。 它包括与 Android 平台交互的工具,主要由和构成,如果您接触过Android开发,必然会使用到它,因为它包含在Android Studio等IDE中,当然,您可以独立下载,在下方选择对应的版本即可: - Download SDK Platform...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值