JZOJ 5781 秘密通道

Description

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

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

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

先说说我之前的解法吧,估计有问题
因为打枪不消耗时间,所以随便走就可以了。
然后我打了搜索
其实完全可以再进一步
向四周连边,再向墙连边,边权为最近的墙的距离。
跑一遍Dijkstra就行,但是注意不能反向边
因为从To到From的情况和From到To是不一样的,To和From所处的位置本就不同

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 505;
char Map[MAXN][MAXN];
int Stx,Sty,Edx,Edy;
int Dis[MAXN * MAXN];
int n,m;
struct _Edge{
    int Next;
    int To;
    int Wei;
} Edge[MAXN * MAXN * 10];
int Head[MAXN * MAXN],Cnt;
void Add(int u,int v,int w) {
    Cnt ++;
    Edge[Cnt].Next = Head[u];
    Edge[Cnt].To = v;
    Edge[Cnt].Wei = w;
    Head[u] = Cnt;
}
struct _Wall{
    int x,y;
} Wall[10];
struct Unit{
    int x;
    int w;
    bool operator < (const Unit & a) const {
        return w > a.w;
    }
};
int Loss[7][7] = {{1,0},{-1,0},{0,1},{0,-1}};
int Vis[MAXN * MAXN];
void Dijkstra() {
    priority_queue<Unit> que;
    memset(Dis,0x3f,sizeof(Dis));
    Dis[(Stx - 1) * n + Sty] = 0;
    que.push((Unit) {
        (Stx - 1) * n + Sty,0
    });
    while(!que.empty()) {
        int x = que.top().x;
        que.pop();
        if(Vis[x]) continue;
        Vis[x] = 1;
        for(int i = Head[x]; i; i = Edge[i].Next) {
            int y = Edge[i].To;
            if(Dis[x] + Edge[i].Wei < Dis[y]) {
                Dis[y] = Dis[x] + Edge[i].Wei;
                if(!Vis[y]) que.push((Unit) {
                    y,Dis[y]
                });
            }
        }
    }
}
int main() {
    freopen("portal.in","r",stdin);
    freopen("portal.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) {
        scanf("%s",Map[i] + 1);
        for(int j = 1; j <= m; j++) {
            if(Map[i][j] == 'C') {
                Stx = i;
                Sty = j;
            }
            if(Map[i][j] == 'F') {
                Edx = i;
                Edy = j;
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            int From = (i - 1) * n + j;
            if(Map[i][j] != '#') {
                int x = i,y = j;
                int tx = x,ty = y,d = 10000;
                while(Map[tx][ty] != '#') tx --;
                Wall[1].x = tx;
                Wall[1].y = ty;
                d = min(d,x - tx);
                tx = x,ty = y;
                while(Map[tx][ty] != '#') tx ++;
                Wall[2].x = tx;
                Wall[2].y = ty;
                d = min(d,tx - x);
                tx = x,ty = y;
                while(Map[tx][ty] != '#') ty --;
                Wall[3].x = tx;
                Wall[3].y = ty;
                d = min(d,y - ty);
                tx = x,ty = y;
                while(Map[tx][ty] != '#') ty ++;
                Wall[4].x = tx;
                Wall[4].y = ty;
                d = min(d,ty - y);
                for(int k = 1; k <= 4; k++) {
                    int To = (Wall[k].x - 1 + Loss[k - 1][0]) * n + Wall[k].y + Loss[k - 1][1];
                    Add(From,To,d);
                }
                for(int k = 0; k < 4; k++) {
                    int tx = i + Loss[k][0],ty = j + Loss[k][1];
                    if(tx < 1 || tx > n || ty < 1 || ty > m || Map[tx][ty] == '#') continue;
                    int To = (tx - 1) * n + ty;
                    Add(From,To,1);
                }
            }
        }
    }
    Dijkstra();
    if(Dis[(Edx - 1) * n + Edy] != 0x3f3f3f3f)
    cout << Dis[(Edx - 1) * n + Edy];
    else cout << "nemoguce";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值