2019 拖拉机(双端队列广搜)

1. 问题描述:

干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了。他的奶牛非常调皮,决定对约翰来场恶作剧。她们在田地的不同地方放了 N 捆干草,这样一来,约翰想要开走拖拉机就必须先移除一些干草捆。拖拉机的位置以及 N 捆干草的位置都是二维平面上的整数坐标点。拖拉机的初始位置上没有干草捆。当约翰驾驶拖拉机时,他只能沿平行于坐标轴的方向(北,南,东和西)移动拖拉机,并且拖拉机必须每次移动整数距离。例如,驾驶拖拉机先向北移动 2 单位长度,然后向东移动 3 单位长度。拖拉机无法移动到干草捆占据的位置。请帮助约翰确定他需要移除的干草捆的最小数量,以便他能够将拖拉机开到二维平面的原点。

输入格式

第一行包含三个整数:N 以及拖拉机的初始位置 (x,y)。接下来 N 行,每行包含一个干草捆的位置坐标 (x,y)。

输出格式

输出约翰需要移除的干草捆的最小数量。

数据范围

1 ≤ N ≤ 50000,
1 ≤ x,y ≤ 1000

输入样例:

7 6 3
6 2
5 2
4 3
2 1
7 3
5 4
6 4

输出样例:

1
来源:https://www.acwing.com/problem/content/description/2021/

2. 思路分析:

首先我们需要读懂题目的意思,题目实际上是给出了一个无限大的二维矩阵,矩阵中有N个位置是有干草的,可以看成是障碍物,拖拉机可以看成是起点,终点为原点,我们需要求解从起点到终点的最短距离,也即需要移开的最少障碍物的数量,由这些特点可以知道为最短路径模型,存在障碍物可以将权重看成是1,否则看成是0所以实际上是边权只有0和1的的单源最短路径模型,边权只有0或者1的最短路径问题可以使用双端队列广搜来解决,单源最短路径可以看成是dijkstra算法的简化版本,因为题目中的矩阵是无限大的,而我们在搜索的时候不可能是搜索无限大的,障碍物的位置为1~1000,所以我们可以在扩展的多扩展一圈或者多扩展几个位置那么就可以了,扩展的这些位置都是没有障碍物的,对于最短路径是没有影响的,这样就可以将搜索的范围控制在0~1010之内了,双端队列广搜类似于堆优化版的dijkstra算法需要借助于一个距离数组dis,用来记录从起点到某个点的最短距离,vis数组用来判重,由于某个点可能入队多次所以应该是在出队的时候进行判重,使用双端队列广搜的模板解决即可。

3. 代码如下:

import collections
from typing import List


class Solution:
    # 双端队列广搜: 堆优化版的dijkstra算法的简化版本
    def bfs(self, sx: int, sy: int, g: List[List[int]]):
        N = 1010
        INF = 10 ** 10
        # dis记录从起点到终点的最短距离
        dis = [[INF] * N for i in range(N)]
        q = collections.deque([(sx, sy)])
        dis[sx][sy] = 0
        # 判重列表
        vis = [[0] * N for i in range(N)]
        # 右左下上四个方向
        pos = [[0, 1], [0, -1], [1, 0], [-1, 0]]
        while q:
            x, y = q.popleft()
            if x == 0 and y == 0: break
            # 出队的时候判重
            if vis[x][y] == 1: continue
            vis[x][y] = 1
            for i in range(4):
                a, b = x + pos[i][0], y + pos[i][1]
                if 0 <= a < N and 0 <= b < N:
                    w = g[a][b]
                    if dis[a][b] > dis[x][y] + w:
                        dis[a][b] = dis[x][y] + w
                        # 边权为1加入到队尾
                        if w:
                            q.append((a, b))
                        # 否则加入到队头
                        else:
                            q.appendleft((a, b))
        return dis[0][0]

    def process(self):
        # n表示障碍物的数量, sx, sy表示起点坐标
        n, sx, sy = map(int, input().split())
        N = 1010
        g = [[0] * N for i in range(N)]
        for i in range(n):
            x, y = map(int, input().split())
            g[x][y] = 1
        return self.bfs(sx, sy, g)


if __name__ == '__main__':
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值