📜  门|门模拟 2017 |问题 13(1)

📅  最后修改于: 2023-12-03 15:42:22.948000             🧑  作者: Mango

题目简介

门|门模拟 2017 |问题 13 是一道经典的编程问题,主要考察程序设计思路和编写能力。该问题涉及到大量的数据结构知识和算法技巧,适合于对编程有一定基础的程序员练习。

题目描述

在一个二维矩形房间里,有一些门。每扇门都有一个起点和一个终点,只有在起点和终点区域内的人才能通过门。现在有一个人从起点出发,他要经过所有的门才能到达终点。请你编写一个程序,计算出这个人需要走的最短距离。

输入格式

第一行输入两个整数 n 和 m,表示门的数量和房间的大小。

接下来的m行,每行表示一个门,包含四个整数x1,y1,x2,y2,表示门的起点和终点坐标。

输出格式

输出一个整数,表示最短走路距离。

注意事项

  • 保证每条路线可行,即保证人可以从起点出发,经过所有门,到达终点。
  • 所有坐标都是 1 到 n 之间的整数。
  • 数据保证存在一条路径,可以从起点出发,经过所有门,到达终点。

解题思路

这道题可以用图论算法来解决,我们可以将每间房间看做图中的一个节点,将门看做边。然后通过最短路算法来计算从起点到终点的最短路径。

可以用 Dijstra 算法或 Bellman-Ford 算法计算最短路径,其中 Dijstra 算法适用于稠密图,时间复杂度为 O(n^2),Bellman-Ford 算法适用于稀疏图,时间复杂度为 O(nm)。同时,为了提高算法效率,我们可以在读入数据时,对门的起点和终点坐标进行离散化,将坐标转换成从 1 开始的自然数,这样可以大大减小算法计算量。

此外,我们还可以使用并查集来判断门是否可达。具体来说,我们将房间看做一个二维坐标系,将门的起点和终点分别转换成一个唯一的编号,然后将所有门的编号插入并查集中。这样,我们可以通过并查集来快速判断两个门是否在同一连通块内,即是否可达。如果两个门在同一连通块内,说明它们之间存在路径,可以直接连边到图中。

代码实现

# 读入数据,将门的起点和终点坐标进行离散化,将坐标转换成从 1 开始的自然数,这样可以大大减小算法计算量。

n, m = map(int, input().split())
edge = []
for i in range(m):
    x1, y1, x2, y2 = map(int, input().split())
    edge.append(((x1-1)*n+y1, (x2-1)*n+y2, abs(x1-x2)+abs(y1-y2)))  # 利用公式计算起点和终点的编号,同时记录它们之间的距离

# 利用并查集来判断门是否可达

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))
        
    def find(self, x):
        while x != self.parent[x]:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x
    
    def union(self, x, y):
        root_x, root_y = self.find(x), self.find(y)
        if root_x != root_y:
            self.parent[root_x] = root_y
            
uf = UnionFind(n*n)
for x1, y1, x2, y2 in edge:
    uf.union(x1-1, x2-1)  # 如果两个门在同一连通块内,说明它们之间存在路径,可以直接连边到图中

# 通过最短路算法计算最短路径

from collections import defaultdict
import heapq

graph = defaultdict(list)
for x1, y1, x2, y2, dist in edge:
    graph[x1].append((y1, x2, y2, dist))
    graph[x2].append((y2, x1, y1, dist))
    
def dijkstra(graph, start, end):
    queue = [(0, start, [])]
    visited = set()
    while queue:
        dist, node, path = heapq.heappop(queue)
        if node == end:
            return dist
        if node in visited:
            continue
        visited.add(node)
        for y1, x2, y2, dist2 in graph[node]:
            if uf.find(x2-1) == uf.find(node-1):  # 如果两个门在同一连通块内,说明它们之间存在路径
                heapq.heappush(queue, (dist+dist2, x2*n+y2, path+[(node, y1, x2, y2)]))

print(dijkstra(graph, 0, n*n-1))

上述代码通过离散化和并查集来降低计算量,使用最短路算法来计算最短路径。时间复杂度为 O(mlogm),其中 m 表示门的数量。