jzoj 5781. 【NOIP提高A组模拟2018.8.8】秘密通道

一、题目

Description
有一副n*m的地图,有n*m块地,每块是下列四种中的一种:
墙:用#表示,墙有4个面,分别是前面,后面,左面,右面。
起点:用C表示,为主角的起点,是一片空地。
终点:用F表示,为主角的目的地,是一片空地。
空地:用 . 表示。
其中除了墙不能穿过,其他地方都能走。

主角有以下3种操作:
1.移动到相邻的前后左右的地方,花费一个单位时间。
2.向前后左右其中一个方向发射子弹,子弹沿直线穿过,打在最近的一堵墙的一面,然后墙的这面就会形成一个开口通往秘密通道。同一时间最多只能有两个开口,若出现有3个开口,出现时间最早的开口会立即消失。该操作不用时间。
3.可以从一个与开口相邻的空地跳进去,进入秘密通道,从另外一个开口正对的空地跳出来。这个过程花费一个单位时间。

地图四周都是墙,问主角最少用多少时间从C走到F。C和F
只会出现一次。

Input
第一行输入两个正整数n,m。
接下来n行,每行m个字符描述地图。

Output
输出1个整数,表示最短时间完成路途。如果无解输出nemoguce

这里写图片描述
Data Constraint
对于50%的数据,4≤ n,m≤ 15。
对于100%的数据,4≤ n,m≤ 500。

二、分析

先吐槽一下。。两次模拟题就这道最简单了但还是改了一天,真的就是太弱啦!
这道题很容易就会想到搜索,但是这道题n有500之多,所以肯定不可能用搜索。
直接先处理可以到哪些地方连边就好了。不是很难,但是一定要自己动手!

三、代码

#include<bits/stdc++.h>
using namespace std;
int n,m;
char a[505][505];
int b_x,b_y,e_x,e_y;
int ans = 1e9;
int tot ;
int head[250005] , ver[2000005] ,edge[2000005] ,Next[2000005] ,d[250005] ;
queue<int> q;
bool v[250005];
void add(int x,int y,int z) {
    ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void spfa(int begin) {
    memset(d,0x3f,sizeof(d));
    memset(v,0,sizeof(v));
    d[begin]=0;
    v[begin]=1;
    q.push(begin);
    while(q.size()) {
        int x=q.front();
        q.pop();
        v[x]=0;
        for(int i=head[x]; i; i=Next[i]) {
            int y=ver[i],z=edge[i];
            if(d[y]>d[x]+z) {
                d[y]=d[x]+z;
                if(!v[y]) q.push(y),v[y]=1;
            }
        }
    }
}
int f_1(int x,int y,int now){
    int be_x = x;
    int be_y = y-now;
    int en_x = x-now;
    int en_y = y;
    while(be_x!=en_x&&be_y!=en_y){
        if(a[be_x][be_y]=='#') return 1;
        be_x --;
        be_y ++;
    }
    return 0;
}
int f_2(int x,int y,int now){
    int be_x = x-now;
    int be_y = y;
    int en_x = x;
    int en_y = y+now;
    while(be_x!=en_x&&be_y!=en_y){
        if(a[be_x][be_y]=='#') return 1;
        be_x ++;
        be_y ++;
    }
    return 0;
}
int f_3(int x,int y,int now){
    int be_x = x;
    int be_y = y+now;
    int en_x = x+now;
    int en_y = y;
    while(be_x!=en_x&&be_y!=en_y){
        if(a[be_x][be_y]=='#') return 1;
        be_x ++;
        be_y --;
    }
    return 0;
}
int f_4(int x,int y,int now){
    int be_x = x+now;
    int be_y = y;
    int en_x = x;
    int en_y = y-now;
    while(be_x!=en_x&&be_y!=en_y){
        if(a[be_x][be_y]=='#') return 1;
        be_x ++;
        be_y --;
    }
    return 0;
}
int much(int x,int y){
    int now=1;
    while(1){
        if(f_1(x,y,now)==1||f_2(x,y,now)==1||f_3(x,y,now)==1||f_4(x,y,now)==1) {
            return now;
        }
        now++;
    }
}
int main(){
//  freopen("portal.in","r",stdin);
//  freopen("portal.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
         cin>>a[i][j];  
         if(a[i][j]=='C') b_x = i , b_y = j ;
         if(a[i][j]=='F') e_x = i , e_y = j ;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]!='#'){
                if(a[i-1][j]!='#') add((i-1)*m+j,(i-2)*m+j,1);
                if(a[i+1][j]!='#') add((i-1)*m+j,(i)*m+j,1);
                if(a[i][j-1]!='#') add((i-1)*m+j,(i-1)*m+j-1,1);
                if(a[i][j+1]!='#') add((i-1)*m+j,(i-1)*m+j+1,1);
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[i][j]!='#'){
            int step = much (i,j) ;
            int dx=i,dy=j;
            while(a[dx][dy]!='#') dx--;
            dx++;
            add((i-1)*m+j,(dx-1)*m+dy,step);
            dx=i,dy=j;
            while(a[dx][dy]!='#') dx++;
            dx--;
            add((i-1)*m+j,(dx-1)*m+dy,step);
            dx=i,dy=j;
            while(a[dx][dy]!='#') dy--;
            dy++;
            add((i-1)*m+j,(dx-1)*m+dy,step);
            dx=i,dy=j;
            while(a[dx][dy]!='#') dy++;
            dy--;
            add((i-1)*m+j,(dx-1)*m+dy,step);
            }   
        }
    }
    spfa((b_x-1)*m+b_y);
    if(d[(e_x-1)*m+e_y]>=99999999) cout<<"nemoguce";
    else cout<<d[(e_x-1)*m+e_y];
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值