2021牛客暑假多校第二场 I题—Penguins

该博客介绍了如何运用宽度优先搜索(BFS)算法解决一个编程竞赛中的企鹅移动问题。在这个游戏中,玩家控制两只企鹅在两个20x20的网格中移动,目标是让它们分别从起点到达终点。由于网格存在障碍物,企鹅的移动受到限制。博主通过BFS实现寻找最短路径,并考虑了企鹅镜像的情况。代码中包含了详细注释,提醒读者注意某些细节。最终,博主给出了完整的解决方案,包括最少步数、路径描述以及企鹅的移动轨迹。

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

2021暑假牛客多校第二场 I题-Penguins(bfs)

思路:
1.bfs
2.因为右边企鹅镜像,所以我们可以给他再镜像一下,这样就可以和左边企鹅同步上下左右了~
3.代码里有很详尽注释哦~看不懂就给你吃逗筋拳
4.while(1)注意细节;
(我注释里有我自己被坑到的细节点,大家可能不一样所以要千万小心呐!这两个小企鹅坏得很)

现在我们就开始看这两个小企鹅吧~

题目描述:

Lovely penguins is a tiny game in which the player controls two penguins.
图片来自csdn另一位博主HeartFireY
(图片来自csdn另一位博主HeartFireY)

The game holds in two 20 \times 2020×20 grids (the left one and the right one), and each has some blocked places.
We number the grid in x^{th} row (starting from the up), y^{th}column (starting from the left) {(x,y)}(x,y).

The penguins move in four directions: up, down, left, right, exactly one step once. If its way is blocked, or it reaches the border, then this movement is omitted.

The player also moves the penguins in four directions, but the behavior of the two penguins is mirrored:
L : left penguin moves to left, right penguin moves to right.
R : left penguin moves to right, right penguin moves to left.
U : both move upwards.
D : both move downwards.
An operation can be omitted on one penguin but works on another.

The left penguin starts from {(20,20)}(20,20) and wants to move to {(1,20)}(1,20).The right penguin starts from {(20,1)}(20,1) and wants to move to {(1,1)}(1,1).If both penguin reach there destination, thay win the game.

Find out the shortest way to win the game.If there are many shortest ways to win, find the one with minimum lexicographical order(D<L<R<U).

Note: When one penguin reaches the destination and the other does not, the penguin that has reached the destination may still move out of the destination.

输入描述:

The input consists of 20 lines, each line contains 41 characters, describing the grids, separated by a space.
‘.’ means the grid is empty.
‘#’ means the grid is blocked.

输出描述:

Output 22 lines.
The first line contains the least number of steps to win the game.
The second line contains a string consisting of ‘L’,‘R’,‘U’,‘D’, describing a way to win.
There may be many ways to win, output the one with minimum lexicographical order.
Then output 20 lines, describing the track of the two penguins, mark the track with character ‘A’, and print as input.

备注:

It’s guaranteed that there always exists a way to win, and the penguins’ initial position is not blocked.

样例我就不丢过来啦,去牛客自测更方便
不然容易把自己看瞎哈哈哈哈哈哈

#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<cstring>
#include<list>
#include<map>
#include<cstdio>
typedef long long ll;
typedef int64_t s64;
using namespace std;
#define maxn 10000007
string a[21],b[21];
bool vis[21][21][21][21]; //vis用于标记该点有没有走过
int xx[5]={1,0,0,-1};     //因为是要按照字典序排序 所以这里要注意顺序是DLRU
int yy[5]={0,-1,1,0};
char lu[5]={'D','L','R','U'};
struct node{
    int x1,y1,x2,y2;
    string path;
};
bool pan(int x,int y,string a[]){
    if(x<0||x>19||y<0||y>19||a[x][y]=='#')
        return 0;
    else
        return 1;
}
string bfs(){
    queue<node>q;
    q.push({19,19,19,19,""});
    vis[19][19][19][19]=1;
    while(!q.empty()){    //当q不为空
        node p=q.front(); //将q第一个元素给p
        q.pop();          //q给完就丢掉第一个元素
        if(p.x1==0&&p.y1==19&&p.x2==0&&p.y2==19)   //如果到终点就返回路径
            return p.path; 
        for(int i=0;i<4;i++){   //这个循环就是往上下左右走探路
            int nx1=p.x1+xx[i],ny1=p.y1+yy[i];  //左边企鹅
            int nx2=p.x2+xx[i],ny2=p.y2+yy[i];  //右边企鹅
            int p1=pan(nx1,ny1,a),p2=pan(nx2,ny2,b);   //判断哪个企鹅能走(调用的函数就是用于判断有没有到边界或者碰到阻碍)
            if(p1&&p2){   //如果两个企鹅都可以走
                if(vis[nx1][ny1][nx2][ny2]) continue;  //如果该点走过就跳过
                vis[nx1][ny1][nx2][ny2]=1;             //如果没有走过就进行标记,并push进我们的q栈
                q.push({nx1,ny1,nx2,ny2,p.path+lu[i]});//还有路径也要push进去哦 (路径就是DLRU这些)
            }
            else if(p1){      //如果只有左企鹅能走 
            //左企鹅用走动后的sx1与sx2 右企鹅则用原始的p.x2与p.y2
                if(vis[nx1][ny1][p.x2][p.y2]) continue; //同上 
                vis[nx1][ny1][p.x2][p.y2]=1;            //同上
                q.push({nx1,ny1,p.x2,p.y2,p.path+lu[i]});
            }
            else if(p2){     //同上(就是左右企鹅反一下)
                //(这你要是不明白我就给你吃逗筋拳)
                if(vis[p.x1][p.y1][nx2][ny2]) continue;  //同上
                vis[p.x1][p.y1][nx2][ny2]=1;   //同上
                q.push({p.x1,p.y1,nx2,ny2,p.path+lu[i]});
            }
        }
    }
    return "";   //如果没有满足的就返回空字符串
}
bool sol(int x,int y,char a,string s[]){   //用于判断是哪个企鹅在走路 在后面输出a(企鹅当前所在位置)的时候用
    if(a=='D'){
        x++;
    }
    else if(a=='L'){
        y--;
    }
    else if(a=='R'){
        y++;
    }
    else if(a=='U'){
        x--;
    }
    if(x<0||x>19||y<0||y>19||s[x][y]=='#')
        return 0;
    return 1;
}
int main(){
    for(int i=0;i<20;i++){
        cin>>a[i]>>b[i]; //输入左右企鹅当前一行的地图
        reverse(b[i].begin(),b[i].end()); //再把右边企鹅的地图镜像
        //(镜像之后才能两个企鹅同步往右走或者往左走)
    }//这样就可以比较容易的来理解这个bfs(就是同时跑两个地图)

    string ans=bfs();
    
    int x1=19,y1=19,x2=19,y2=19;
    for(int i=0;i<ans.size();i++){ 
    //这个循环就是用于记录企鹅当前在什么位置(cal函数的作用就体现出来啦!)
        if(sol(x1,y1,ans[i],a)){
            if(ans[i]=='D')
                x1++;
            else if(ans[i]=='L')
                y1--;
            else if(ans[i]=='R')
                y1++;
            else if(ans[i]=='U')
                x1--;
            a[x1][y1]='A';
        }
        if(sol(x2,y2,ans[i],b)){
            if(ans[i]=='D')
                x2++;
            else if(ans[i]=='L')
                y2--;
            else if(ans[i]=='R')
                y2++;
            else if(ans[i]=='U')
                x2--;
            b[x2][y2]='A';
        }
    }
    a[19][19]=b[19][19]=a[0][19]=b[0][19]='A';//细节点:不要忘记标记企鹅的起点与终点
    //(不然他就是灵异企鹅 突然出现突然消失~~ 可怕哎!)
    
    cout<<ans.size()<<endl;//先输出步数(恭喜恭喜这是我们得出的第一个答案!)
    cout<<ans<<endl;       //再输出我们的路径就是一堆DLRU的东西
    for(int i=0;i<20;i++){
     //细节点:这里要记得把我们的右边企鹅的镜像地图再翻转回来哦!!!
        reverse(b[i].begin(),b[i].end());
    }
    for(int i=0;i<20;i++){
     //这里就是输出我们最后的整张地图啦!
        cout<<a[i]<<" "<<b[i]<<endl;
    }
    return 0;//看到这里恭喜你 你要ac啦!
}
/* 样例丢在这里啦~
#................... .............##...#.
.................... .......#.....#.....#
.........#...#.#.... ...#....#...........
#........#.......... ...#..#.............
........#......#.... ..#.#......#.#.....#
......#.#..#.#....#. .......##.....##...#
....#...........#..# ....................
.##................. ...........#..#...#.
.....#.#........#.#. #.........#.#.......
.................... ..#....#..........#.
....#.#..........#.. .#.........#..#..#..
.........#.......#.. ..#.................
...#..#......#...#.. ......#.............
...........#...#.... ....................
..##..#.#....#..#... ..............#...#.
.#..#...#.#.....##.. .........#.#...#....
.#.........#........ ..............#.#...
..##.#........#...#. ##..................
....##.#............ .......#.....#......
..........##........ .#..#.#...........#.
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值