九校联考-长沙市一中NOIP模拟Day2T2 走格子(cell)

问题描述

CYJ想找到他的小伙伴FPJ,.CYJ和FPJ现在位于一个房间里,这个房间的布置可以看成一个N行M列的矩阵,矩阵内的每一个元素会是下列情况中的一种:

  1. 障碍区域—这里有一堵墙(用‘#’表示).
  2. 这是CYJ最开始在的区域(用‘C’表示).
  3. 这是FPJ在的区域(用‘F’表示).
  4. 空区域(用‘.’表示).
    CYJ携带了一个所谓传送枪的东西,这是一把可以创造传送门的枪械,在每一次行动中,他可以选择下列操作中的一项:
  5. 移向一个相邻的格子中(上,下,左,右,不能移到墙在的格子里).这个操作要消耗一个单位的时间.
  6. 转向一个墙(不需要相邻,只需面向即可),向其发射传送门,传送门会留在墙内面向你的地方(至多只能同时存在两扇传送门),若墙上已经有两扇传送门,而你发射了第三扇,那么最初发射的那一扇会消失。同时,你无法在一个位置制造两扇传送门(这个操作不会耗费时间)。
  7. 如果他与一块墙壁相邻且面前有一扇传送门,那么他可以移动到另一扇传送门前方的格子。这个操作会耗费一个单位的时间.
    CYJ想要知道自己最少需要多少时间才能够从起点(‘C’)到达终点(‘F’).
    请注意:我们保证地图边缘会是一圈墙壁且一定存在‘C’,‘F’.

输入

第一行输入两个正整数 N 和 M ,(4 ≤ N,M ≤ 500).表示地图大小。
接下来的N行每行一个长度为M的字符串.表示地形。

输出

你需要输出最少的到达终点的时间,如果不能到达请输出”no”。

先BFS搜出每个点到墙(或到旁边有墙的点)的距离,同时用这个距离建图,建完图跑diji或spfa得出正解

#include<bits/stdc++.h>
using namespace std;

const int Maxn = 505;
const int MX = 3e5+5; 
const int inf = 1e9;

int N,M,S,T,cnt;
int md[Maxn][Maxn],Lm[Maxn][Maxn],Rm[Maxn][Maxn];
int Dm[Maxn][Maxn],Um[Maxn][Maxn],hed[MX];
int nxt[MX<<4],to[MX<<4],len[MX<<4],dis[MX];
char mp[Maxn][Maxn]; 
struct  data{
    int x , y;
    bool operator <(const data &A) const{
        return y > A.y;
    }
};
 
queue<data>Q;
 
inline int id(int x,int y){return (x - 1)*M + y;}

void inc(int x,int y,int d){
    if(mp[x][y] == '.' && !md[x][y])
        md[x][y] = d , Q.push((data){x,y}); 
}

void Bfs(){
    while(!Q.empty()){
        int x = Q.front().x,y = Q.front().y;
        Q.pop();
        inc(x - 1 , y , md[x][y] + 1);
        inc(x , y - 1 , md[x][y] + 1);
        inc(x + 1 , y , md[x][y] + 1);
        inc(x , y + 1 , md[x][y] + 1); 
    }
}

void Adg(int x,int y,int l){
    nxt[++cnt] = hed[x]; to[cnt] = y; 
    hed[x] = cnt; len[cnt] = l; 
}

void Addedge(int x,int y,int l){
    Adg(x , y , l); Adg(y , x , l);
}

void init(){
    for(int i = 1 ; i <= N ; ++i)
    for(int j = 1 ; j <= M ; ++j) if(mp[i][j] == '.'){
        if(mp[i][j-1] == '#') Lm[i][j] = id(i , j);
        else Lm[i][j] = Lm[i][j - 1];
        if(mp[i-1][j] == '#') Um[i][j] = id(i , j);
        else Um[i][j] = Um[i - 1][j]; 
    } 
    
    for(int i = N ; i ; --i)
    for(int j = M ; j ; --j) if(mp[i][j] == '.'){
        if(mp[i][j+1] == '#') Rm[i][j] = id(i,j);
        else Rm[i][j] = Rm[i][j + 1];
        if(mp[i+1][j] == '#') Dm[i][j] = id(i,j);
        else Dm[i][j] = Dm[i + 1][j]; 
    }
}

void Build(){
    for(int i = 1 ; i <= N ; ++i)
    for(int j = 1 ; j <= M ; ++j) if(mp[i][j] == '.'){
        if(mp[i][j + 1] == '.') Addedge(id(i,j),id(i,j+1),1);
        if(mp[i + 1][j] == '.') Addedge(id(i,j),id(i+1,j),1);
        if(Lm[i][j] != id(i , j)) Adg(id(i,j),Lm[i][j],md[i][j]);
        if(Rm[i][j] != id(i , j)) Adg(id(i,j),Rm[i][j],md[i][j]);
        if(Um[i][j] != id(i , j)) Adg(id(i,j),Um[i][j],md[i][j]);
        if(Dm[i][j] != id(i , j)) Adg(id(i,j),Dm[i][j],md[i][j]);  
    }
}

void Dijkstra(){
    priority_queue<data>PQ;
    for(int i = 1 ; i <= N*M ; ++i)
        dis[i] = inf; 
    dis[S] = 0; PQ.push((data){S,dis[S]});
    while(!PQ.empty()){
        int x = PQ.top().x,L = PQ.top().y;
        PQ.pop();
        if(L > dis[x]) continue;
        for(int i = hed[x],v ; i ; i = nxt[i])
            if(dis[v = to[i]] > dis[x] + len[i]){
                //if(v == T) printf("%d\n",x);
                dis[v] = dis[x] + len[i];
                PQ.push((data){v,dis[v]}); 
            }
    }
}

int main(){
    freopen("cell.in","r",stdin);
    freopen("cell.out","w",stdout);
    scanf("%d%d",&N,&M);
    for(int i = 1 ; i <= N ; ++i)
        scanf("%s" , mp[i] + 1);
    for(int i = 1 ; i <= N ; ++i)
    for(int j = 1 ; j <= M ; ++j){
        if(mp[i][j] == 'C') mp[i][j] = '.',S = id(i,j);
        if(mp[i][j] == 'F') mp[i][j] = '.',T = id(i,j);
    }
    for(int i = 1 ; i <= N ; ++i)
    for(int j = 1 ; j <= M ; ++j)
        if(mp[i][j] == '#') Q.push((data){i,j});
    Bfs(); init(); 
    Build(); Dijkstra();
    if(dis[T] != inf) printf("%d\n",dis[T]);
    else printf("no\n");
    
    return 0;
}

转载于:https://www.cnblogs.com/shulker/p/9663138.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值