Labyrinth(2020 CCPC 威海 B)


原题链接

题目大意

走迷宫,一个n*m的图,有k个地方毁坏(称为黑洞),有q次询问:问(x1,y1)到(x2,y2)的最小移动次数

思路

显然,暴力跑q边bfs不行!然后就看见42个黑洞点。想着用这42个黑洞点。以这42个黑洞点跑bfs就可以了!但是,下面这个图怎么处理呢?(蓝色部分为黑洞)

在这里插入图片描述
答案:特判呗!以u,v作为对顶点的矩形内是否有黑洞,如果没有直接就是曼哈顿距离,否则就以黑洞点跑bfs

注意:在走路的时候不能经过黑洞,所以以黑洞周围的四个点为准跑bfs。

然后就是建图方面!N和M的范围决定了建图有大问题,这里采用new int[] 的方法!

所以,总体解题思路:
属于一道bfs题。分为两点
1、当以起止点作为对顶点所围成的矩形范围内不存在黑洞,最小距离就是曼哈顿距离
2、如果存在黑洞,那么从起始点一定有一条边可以经过黑洞达到终止点。以黑洞四周的四个点维中间点,做bfs找最小距离。
复杂度:O( 164 n m 164nm 164nm

代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <math.h>
using namespace std;

typedef pair<int, int> pii;
const int inf = 0x3f3f3f;

int n,m,k,t;
int mov[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};//移动方向

int *dist[50][5][200010];//记录距离(需要动态申请空间)
map<pii, int> vis;//标记坐标
set<pii> helo;//记录黑洞的地点

void bfs(pii s)
{
    int idx = vis[s];
    for(int i = 0 ;i < 4; i++)//便利黑洞周围的四个点
    {
        int dx = s.first + mov[i][0];
        int dy = s.second + mov[i][1];
        if(dx < 1 || dy < 1 || dx > n || dy > m) continue;//判断越界
        if(helo.count(make_pair(dx, dy))) continue;//判断是否为黑洞

        for(int k = 1; k <= n; k++)
        {
            dist[idx][i][k] = new int[m + 5];//动态申请空间
            for(int j = 1; j <= m; j++)
                dist[idx][i][k][j] = inf;
        }
        dist[idx][i][dx][dy] = 0;
        queue<pii> q;//开始走bfs
        q.push({dx,dy});
        while(!q.empty())
        {
            pii u = q.front();
            q.pop();
            for(int j = 0; j < 4; j++)//向四周走
            {
                int x = u.first + mov[j][0];
                int y = u.second + mov[j][1];
                
                if(x < 1 || y < 1 || x > n || y > m) continue;
                if(helo.count(make_pair(x, y))) continue;
                
                if(dist[idx][i][x][y] > dist[idx][i][u.first][u.second] + 1)
                {
                    dist[idx][i][x][y] = dist[idx][i][u.first][u.second] + 1;
                    q.push({x,y});
                }
            }
        }
    }
}

bool fid(pii mid, pii u,pii v)//判断黑洞在不在矩形范围内(这里写的很复杂,但是好理解)
{
    if(u.first >= mid.first && u.second >= mid.second && v.first <= mid.first && v.second <= mid.second) return false;
    if(u.first <= mid.first && u.second <= mid.second && v.first >= mid.first && v.second >= mid.second) return false;
    if(u.first >= mid.first && u.second <= mid.second && v.first <= mid.first && v.second >= mid.second) return false;
    if(u.first <= mid.first && u.second >= mid.second && v.first >= mid.first && v.second <= mid.second) return false;
    return true;
}
bool jud(pii u, pii v)
{
    for(auto it = helo.begin(); it != helo.end(); it++)
    {
        if(!fid((*it), u, v))
            return false;
    }
    return true;
}


int main()
{
    scanf("%d%d%d%d",&n,&m,&k,&t);
    for(int i = 0; i < k; i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        helo.insert({x,y});
        vis[{x,y}] = i;
    }
    
    for(auto it = helo.begin(); it != helo.end(); it++) bfs(*it);//依次更新每一个黑洞的bfs
    
    while(t--)
    {
        int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        pii x = {x1,y1}, y = {x2,y2};
        
        if(jud(x,y))
        {
            printf("%d\n",(int)(fabs(x1 - x2) + fabs(y1 - y2)));
            continue;
        }
        
        int ans = inf;
        for(auto it = helo.begin(); it != helo.end(); it++)
        {
            int idx = vis[(*it)];
            for(int i = 0 ;i < 4; i++)
            {
                int dx = (*it).first + mov[i][0];
                int dy = (*it).second + mov[i][1];
                
                if(dx < 1 || dy < 1 || dx > n || dy > m) continue;
                if(helo.count(make_pair(dx, dy))) continue;
                
                ans = min(ans, dist[idx][i][x1][y1] + dist[idx][i][x2][y2]);
            }
        }
        printf("%d\n",ans == inf ? -1 : ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值