[题解]2024 ICPC台中(中国台湾)区域赛-Drunken Maze

  • Sources:D-Drunken Maze
  • Abstract:给定 n × m ( 12 ≤ n × m ≤ 2 × 1 0 5 , 3 ≤ n , m ≤ 1 0 4 ) n\times m(12\le n\times m\le 2\times 10^5, 3\le n,m\le 10^4) n×m(12n×m2×105,3n,m104)的地图,# 表示墙壁,. 表示空地,S 表示起点,T 表示终点。要求不能超过连续 3 3 3步走同一方向,不可以通过障碍。起点终点视作空地。问 ST 的最小步数。若无解输出 − 1 -1 1
  • Keyword:搜索(铜牌题)
  • Solution:注意到 n n n m m m非常大,因此无法用结构体存图。改用动态处理队列。设计状态数组 d i s t dist dist,注意到图中每个点可通过求解其 i d id id进而将状态数组压至三维。 d i s t [ i d ] [ d i r ] [ c n t ] dist[id][dir][cnt] dist[id][dir][cnt]代表沿 d i r dir dir方向上经过 c n t cnt cnt次到达顶点 i d id id。注意处理当 c n t cnt cnt到达 3 3 3时不可继续沿此方向继续走。
  • Code:实现上可采用 B F S BFS BFS,或 D F S DFS DFS+记忆化。下面给出 B F S BFS BFS实现。
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e4;
const int INF=1e9;
char mapp[MAX][MAX];
int n,m,sy,sx,ty,tx;
struct node{
    int y,x,pre_dir,cnt;//pre_dir:上一步自何方向而来,0-4 cnt:方向上已经走了多少步,0-2
};
queue<node>q;
int getid(int y,int x){//计算顶点坐标
    return y*m+x;
}
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};//(y,x):right,left,down,up
bool check1(int y,int x){//越界/撞墙检测
    return y>=0&&y<n&&x>=0&&x<m&&mapp[y][x]!='#';
}
bool check2(int y,int x){//终点检测
    return x==tx&&y==ty;
}
void bfs(){
    vector<vector<vector<int>>>dis(n*m,vector<vector<int>>(4,vector<int>(3,INF)));//状态数组,三个维度分别表示id,dir,cnt,初始化所有点均为不可达
    for(int i=0;i<4;i++){//处理起点的四个方向
        int ny=sy+dir[i][0],nx=sx+dir[i][1];
        if(check1(ny,nx)){//越界/撞墙检测
            int id=getid(ny,nx);
            if(dis[id][i][0]>1){//起点扩散的点一定可达
                dis[id][i][0]=1;
                q.push({ny,nx,i,1});
            }
        }
    }
    bool f=0;//判断是否有解
    while(q.size()){
        int ans=dis[getid(q.front().y,q.front().x)][q.front().pre_dir][q.front().cnt-1];
        if(check2(q.front().y,q.front().x)){//已到达终点
            cout<<ans,f=1;
            break;
        }
        for(int i=0;i<4;i++){//考察当前点的四个方向
            int now=0;//记录当前方向上走的步数
            if(i==q.front().pre_dir){//沿当前方向继续走
                now=q.front().cnt+1;
                if(now>3) continue;//不能继续走了
            }else now=1;//未沿当前方向继续走
            int ny=q.front().y+dir[i][0],nx=q.front().x+dir[i][1];
            if(check1(ny,nx)){//越界/撞墙检测
                if(dis[getid(ny,nx)][i][now-1]>ans+1){//可以扩散的点一定可达
                    dis[getid(ny,nx)][i][now-1]=ans+1;
                    q.push({ny,nx,i,now});
                }
            }
        }
        q.pop();
    }
    if(!f) cout<<-1;//无解
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n>>m;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++){
            cin>>mapp[i][j];
            //记录特殊点S和T的位置
            if(mapp[i][j]=='S') sy=i,sx=j;
            else if(mapp[i][j]=='T') ty=i,tx=j;
        }
    bfs();
    return 0;
}
posted @   椰萝Yerosius  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示