广度优先搜索进阶

Luogu P1374

题目传送门

刚刚翻题解区发现我的做法和一些神犇有亿点相似…?不会是老师给我们讲题之前看过题解吧…(老师您可千万别看见我


这道题是在地图上的操作,四个方向,因此大概率是搜索。求的是和魔王相遇所需的最短时间,刚好和广搜十分吻合。如果对广搜还有疑惑,建议先去看看p1443,是很好的广搜模板题。

基本思路很简单,模拟萨尔和魔王的位置变化,同时循环模拟出小A所有可能的移动,当小A与魔王位置相同时,输出总时间。


基本地,用bool类型的mark数组存储是否走过的信息,小A每次移动后都要标记走过。用char类型的map数组存储地图,因为输入的01矩阵没有空格,如果用int类型的话每行就会被存成一个整数。

额外的,表示萨尔和魔王移动的数字串要用字符数组或字符串来存储,原因同上,同时在取第i位时要注意将字符类型转化为整数类型。


首先定义一个结构体info,存储3人的基本信息

struct info
{
    int a_x, a_y;//小A的位置
    int s_x, s_y;//萨尔的位置
    int devil_x, devil_y;//魔王的位置
    int t, t1;//总秒数, 超出光环的秒数
};

如果在光环内,就把t1赋值为0,否则就在上一次移动的基础上+1


然后用两个函数分别模拟萨尔和魔王的移动。注意,如果数字串当前位为0就原地不动,如果移动后会超出边界也要原地不动。

info master_move(info temp, int master){
    if(master == 0)return temp;//当前数字是0,原地不动
    if(master == 1){//向上走
        int xx = temp.s_x - 1;
        int yy = temp.s_y; 
        if(!check(xx, yy))return temp;
        temp.s_x = xx;
        return temp;
    }
    else if(master == 2){//向下走
        int xx = temp.s_x + 1;
        int yy = temp.s_y;
        if(!check(xx, yy))return temp;
        temp.s_x = xx;
        return temp;
    }
    else if(master == 3){//向左走
        int xx = temp.s_x;
        int yy = temp.s_y - 1;
        if(!check(xx, yy))return temp;
        temp.s_y = yy;
        return temp;
    }
    else {//向右走
        int xx = temp.s_x;
        int yy = temp.s_y + 1;
        if(!check(xx, yy))return temp;
        temp.s_y = yy;
        return temp;
    }
}

魔王和萨尔的函数几乎相同,在此不多赘述,只是要注意变量名的变化。


AC代码

#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
#include <algorithm>
#include <queue>

using namespace std;
int n, m, s, d;//n * m, 超出光环的秒数, 光环笼罩的范围
int _x, _y, x2, y2;
bool mark[55][55];
//方向数组
int cx[5] = {0, 1, -1, 0, 0};
int cy[5] = {0, 0, 0, 1, -1};
string sal, devil;

struct info
{
    int a_x, a_y;//小A的位置
    int s_x, s_y;//萨尔的位置
    int devil_x, devil_y;//魔王的位置
    int t, t1;//总秒数, 超出光环的秒数
};
queue <info> line;
char map[55][55];

double howfar(info m){//小A和萨尔之间的距离
    double x = sqrt(pow(m.a_x - m.s_x, 2) + pow(m.a_y - m.s_y, 2));//距离公式
    return x;
}

bool check(int x, int y){
    //如果这个点超出了边界,或者已经走过,或者是障碍,那么就不可以走
    if(x > n || x <= 0 || y > m || y <= 0 || mark[x][y] == 1 || map[x][y] == '1')return 0;
    return 1;
}

info master_move(info temp, int master){
    if(master == 0)return temp;//当前数字是0,原地不动
    if(master == 1){//向上走
        int xx = temp.s_x - 1;
        int yy = temp.s_y; 
        if(!check(xx, yy))return temp;
        temp.s_x = xx;
        return temp;
    }
    else if(master == 2){//向下走
        int xx = temp.s_x + 1;
        int yy = temp.s_y;
        if(!check(xx, yy))return temp;
        temp.s_x = xx;
        return temp;
    }
    else if(master == 3){//向左走
        int xx = temp.s_x;
        int yy = temp.s_y - 1;
        if(!check(xx, yy))return temp;
        temp.s_y = yy;
        return temp;
    }
    else {//向右走
        int xx = temp.s_x;
        int yy = temp.s_y + 1;
        if(!check(xx, yy))return temp;
        temp.s_y = yy;
        return temp;
    }
}
info lord_move(info temp, int lord){
    if(!lord)return temp;//当前数字是0,原地不动
    if(lord == 1){//向上走
        int xx = temp.devil_x - 1;
        int yy = temp.devil_y;
        if(!check(xx, yy))return temp;
        temp.devil_x = xx;
        return temp;
    }
    else if(lord == 2){//向下走
        int xx = temp.devil_x + 1;
        int yy = temp.devil_y;
        if(!check(xx, yy))return temp;
        temp.devil_x = xx;
        return temp;
    }
    else if(lord == 3){//向左走
        int xx = temp.devil_x;
        int yy = temp.devil_y - 1;
        if(!check(xx, yy))return temp;
        temp.devil_y = yy;
        return temp;
    }
    else {//向右走
        int xx = temp.devil_x;
        int yy = temp.devil_y + 1;
        if(!check(xx, yy))return temp;
        temp.devil_y = yy;
        return temp;
    }
}

int main(){

    freopen("code.in", "r", stdin);
    freopen("code.out", "w", stdout);

    cin >> n >> m >> s >> d;
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            cin >> map[i][j];
        }
    
    cin >> _x >> _y >> x2 >> y2;
    mark[_x][_y] = true;
    getline(cin, sal);//getline会读入上一行的回车,所以要用两次getline覆盖
    getline(cin, sal);
    getline(cin, devil);

    info point;
    point.a_x = point.s_x = _x;
    point.a_y = point.s_y = _y;
    point.devil_x = x2;
    point.devil_y = y2;
    point.t = point.t1 = 0;
    line.push(point);

    while(!line.empty()){
        info point = line.front();
        line.pop();
        point.t++;//总时间+1

        int master, lord;
        //字符串中第t位,并转为整数类型
        master = sal[(point.t - 1) % sal.size()] - '0';
        lord = devil[(point.t - 1) % devil.size()] - '0';

        info now;
        now = master_move(point, master);//更新萨拉的位置
        now = lord_move(point, lord);//更新魔王的位置
        //魔王移动后判断小A与魔王的相对位置
        if(now.a_x == now.devil_x && now.a_y == now.devil_y){
            cout << now.t << endl;
            return 0;
        }
        
        //小A开始移动
        for(int i = 1; i <= 4; i++){//小A可以向四个方向移动,每次一个单位长度
            //移动后坐标
            int xx = point.a_x + cx[i];
            int yy = point.a_y + cy[i];
            if(check(xx, yy)){//可以移动到这个坐标
                now.a_x = xx;
                now.a_y = yy;
                if(howfar(now) <= d){//在光圈内
                    info next = now;
                    next.t1 = 0;//超出光圈的时间清零
                    mark[xx][yy] = 1;//标记走过
                    line.push(next);
                    if(now.a_x == now.devil_x && now.a_y == now.devil_y){//如果小A遇到魔王
                        cout << now.t << endl;
                        return 0;
                    }
                }
                else if(now.t > s){//超出存活时间,不可继续
                    continue;
                }
                else {//不在光圈内,但是仍在存活时间内。仍可添加新节点
                    info next = now;
                    next.t1 = point.t1 + 1;//光圈外活动时间+1
                    line.push(next);
                    mark[xx][yy] = 1;
                    if(now.a_x == now.devil_x && now.a_y == now.devil_y){//如果小A遇到魔王
                        cout << now.t << endl;
                        return 0;
                    }
                }
            }
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值