迷宫2 ——单调队列解决bfs问题

链接:https://ac.nowcoder.com/acm/problem/15196
来源:牛客网

题目描述

这是一个关于二维格子状迷宫的题目。迷宫的大小为N*M,左上角格子座标为(1,1)、右上角格子座标为(1,M)、左下角格子座标为(N,1)、右下角格子座标为(N,M)。每一格都用-1到109之间的整数表示,意义分别为:-1为墙壁,0为走道,而1到109之间的正整数代表特殊的走道。
蜥蜴最初位于迷宫的座标(1,1)的格子,每一步蜥蜴只能往上、下、左、右、左上、右上、左下、右下八个方向之一前进一格,并且,他也不能走出迷宫边界。蜥蜴的目的地是走到迷宫的右下角格子,也就是座标位置(N,M)。我们想要动一些手脚,使得蜥蜴没有办法从(1,1)出发并抵达(N,M)。我们学会了一个邪恶的法术,这个法术可以把特殊的走道变成墙壁,施法一次的代价为表示该特殊走道的正整数。
假设,我们可以在蜥蜴出发之前不限次数的使用这个邪恶的法术,所花的总代价即为每次施法代价的总和,蜥蜴出发之后就不能再使用这个法术了,请问让蜥蜴没办法达到终点所必须花费的最小总代价是多少呢?
注意,0所代表的走道是无法变为墙壁的。

输入描述

输入的第一行有三个正整数Q,N,M。
代表接下来有Q组数据,这Q组数据都是N*M的迷宫。
接下来每组数据各N行,代表一个迷宫,每行各M个整数,第i行中的第j个整数代表迷宫座标(i,j)的格子。

输出描述

每一组数据输出一行,如果无论如何蜥蜴都能到达终点,请在这一行中输出-1,否则请在这一行中输出一个代表答案的整数。

数据范围

1<=Q<=5*103
1<=Q*N*M<=2.5*105
1<=N,M<=500
代表迷宫格子的数字为介于-1和109间的整数(包含-1和109)
每个迷宫中,代表座标(1,1)和(N,M)的格子的数字一定是0

分析

这道题比较有意思了,大体意思就是我们需要将一些格子堵上,然后图中没有任何路线,所以我们想的是 ,建墙

因为是从左上角前往右下角,所以我们可以从左边墙壁往上面墙壁或者右边墙壁建立围墙,也可以从下面墙壁往上面墙壁或者右边墙壁建立围墙,结合这道题的数据范围可以用有限队列优化一下,写法跟堆优化版的Dijkstra算法有点类似

然后需要注意一下数据范围,需要开到long long这么大,如果过了55%的点的话大概率就是ans开的int范围,如果过了95%的点的话应该就是一开始INF赋值的时候少了,这道题的数据范围差不多到了long long的边界了

代码

#include <queue>
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;

struct node{
    ll d;
    int x,y;
    friend bool operator < (node a,node b)
    {
        return a.d > b.d;
    }
};

const int N = 510;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll map[N][N];
ll d[N][N];
int n,m,q;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};

int main(){
    scanf("%d%d%d",&q,&n,&m);
    while(q--){
        priority_queue<node> Q;
        for(int i = 1;i <= n;i++)   
            for(int j = 1;j <= m;j++){
                scanf("%lld",&map[i][j]);
                if(map[i][j] == -1) map[i][j] = 0;
                else if(map[i][j] == 0) map[i][j] = -1;
                d[i][j] = INF;
            }
        for(int i = 1;i <= n;i++){
            if(map[i][1] == -1) continue;
            Q.push({map[i][1],i,1});
            d[i][1] = map[i][1];
        }
        for(int i = 1;i <= m;i++){
            if(map[n][i] == -1) continue;
            Q.push({map[n][i],n,i});
            d[n][i] = map[n][i];
        }
        ll ans = -1;
        while(!Q.empty()){
            auto t = Q.top();
            Q.pop();
            if(t.x == 1 || t.y == m){
                ans = t.d;
                break;
            }
            for(int i = 0;i < 4;i++){
                int a = t.x + dx[i];
                int b = t.y + dy[i];
                if(a < 1 || a > n || b < 1 || b > m) continue;
                if(map[a][b] == -1) continue;
                if(d[a][b] > t.d + map[a][b]){
                    d[a][b] = t.d + map[a][b];
                    Q.push({d[a][b],a,b});
                }
            }
        }
        printf("%lld\n",ans);
    }
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值