結果
問題 | No.1069 電柱 / Pole (Hard) |
ユーザー | matumoto |
提出日時 | 2022-07-25 04:47:58 |
言語 | Python3 (3.12.2 + numpy 1.26.4 + scipy 1.12.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 12,732 bytes |
コンパイル時間 | 90 ms |
コンパイル使用メモリ | 13,824 KB |
実行使用メモリ | 27,656 KB |
最終ジャッジ日時 | 2024-07-07 01:15:47 |
合計ジャッジ時間 | 3,963 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 47 ms
19,616 KB |
testcase_01 | AC | 42 ms
12,672 KB |
testcase_02 | AC | 42 ms
12,672 KB |
testcase_03 | AC | 41 ms
12,416 KB |
testcase_04 | TLE | - |
testcase_05 | -- | - |
testcase_06 | -- | - |
testcase_07 | -- | - |
testcase_08 | -- | - |
testcase_09 | -- | - |
testcase_10 | -- | - |
testcase_11 | -- | - |
testcase_12 | -- | - |
testcase_13 | -- | - |
testcase_14 | -- | - |
testcase_15 | -- | - |
testcase_16 | -- | - |
testcase_17 | -- | - |
testcase_18 | -- | - |
testcase_19 | -- | - |
testcase_20 | -- | - |
testcase_21 | -- | - |
testcase_22 | -- | - |
testcase_23 | -- | - |
testcase_24 | -- | - |
testcase_25 | -- | - |
testcase_26 | -- | - |
testcase_27 | -- | - |
testcase_28 | -- | - |
testcase_29 | -- | - |
testcase_30 | -- | - |
testcase_31 | -- | - |
testcase_32 | -- | - |
testcase_33 | -- | - |
testcase_34 | -- | - |
testcase_35 | -- | - |
testcase_36 | -- | - |
testcase_37 | -- | - |
testcase_38 | -- | - |
testcase_39 | -- | - |
testcase_40 | -- | - |
testcase_41 | -- | - |
testcase_42 | -- | - |
testcase_43 | -- | - |
testcase_44 | -- | - |
testcase_45 | -- | - |
testcase_46 | -- | - |
testcase_47 | -- | - |
testcase_48 | -- | - |
testcase_49 | -- | - |
testcase_50 | -- | - |
testcase_51 | -- | - |
testcase_52 | -- | - |
testcase_53 | -- | - |
testcase_54 | -- | - |
testcase_55 | -- | - |
testcase_56 | -- | - |
testcase_57 | -- | - |
testcase_58 | -- | - |
testcase_59 | -- | - |
testcase_60 | -- | - |
testcase_61 | -- | - |
testcase_62 | -- | - |
testcase_63 | -- | - |
testcase_64 | -- | - |
testcase_65 | -- | - |
testcase_66 | -- | - |
testcase_67 | -- | - |
testcase_68 | -- | - |
testcase_69 | -- | - |
testcase_70 | -- | - |
testcase_71 | -- | - |
testcase_72 | -- | - |
testcase_73 | -- | - |
testcase_74 | -- | - |
testcase_75 | -- | - |
testcase_76 | -- | - |
testcase_77 | -- | - |
testcase_78 | -- | - |
testcase_79 | -- | - |
testcase_80 | -- | - |
testcase_81 | -- | - |
testcase_82 | -- | - |
ソースコード
# verification-helper: PROBLEM https://yukicoder.me/problems/no/1069 import sys import heapq import copy import math from typing import Dict, List, Set, Tuple # Pointクラス # コンストラクタで(x,y)に代入可能(指定しなかったら(0,0)の地点) class Point: def __init__(self, x: float = 0, y: float = 0, idx: int = 0): self.x = x self.y = y self.idx = idx # otherがPoint型でないならNotImplementedエラーを返す(標準にある) def __eq__(self, other) -> bool: if not isinstance(other, Point): return NotImplemented is_same_x = utility.equals(self.x, other.x) is_same_y = utility.equals(self.y, other.y) return is_same_x and is_same_y def __lt__(self, other) -> bool: return (self.x < other.x) or (self.x == other.x and self.y < other.y) # -self def __neg__(self): return Point(-self.x, -self.y) # self + Point def __add__(self, other): if not isinstance(other, Point): return NotImplemented return Point(self.x + other.x, self.y + other.y) # self - Point def __sub__(self, other): if not isinstance(other, Point): return NotImplemented return self + (-other) # self * Point def __mul__(self, other): if not isinstance(other, Point): return NotImplemented return Point((self.x * other.x) + (self.y * other.y), (self.x * other.y) + (self.y * other.x)) # self * int # self * float def __mul__(self, other): if not (isinstance(other, int) or isinstance(other, float)): return NotImplemented return Point(self.x * other, self.y * other) # set用のhash、重複した地点はないことを前提にidxを参照してます。 def __hash__(self): return hash(self.idx) # idxのセッター def set_idx(self, idx): self.idx = idx # -pi<=a<=pi となる角度a[rad]を返す def radian(p: Point) -> float: return math.atan2(p.y, p.x) # -180<=a<=180 となる角度a[deg]を返す def degree(p: Point) -> float: return math.degrees(math.atan2(p.y, p.x)) # x*x+y*yを返す(ノルム) def norm(p: Point) -> float: return p.x * p.x + p.y * p.y # 絶対値|p|を返す def abs(p: Point) -> float: return math.sqrt(norm(p)) # pをradだけ(0,0)を中心に回転させる def rotate(p: Point, rad: float) -> Point: return Point(math.cos(rad) * p.x + math.sin(-rad) * p.y, math.sin(rad) * p.x + math.cos(-rad) * p.y) def distance_pp(p1: Point, p2: Point) -> float: return abs(p1 - p2) # 十分に小さい数 def eps() -> float: return pow(10, -7) # 相対誤差がeps() def equals(a: float, b: float) -> bool: return math.fabs(a - b) < eps() # 符号を調べる def sign(a: float) -> int: if a > eps(): return +1 if a < -eps(): return -1 return 0 class Edge: def __init__(self, from_idx: int = 0, to_idx: int = 0, cost: float = 0): self.from_idx = from_idx self.to_idx = to_idx self.cost = cost def __eq__(self, other) -> bool: if not isinstance(other, Edge): return NotImplemented same1 = self.from_idx == other.from_idx same2 = self.to_idx == other.to_idx same3 = self.cost == other.cost return same1 and same2 and same3 def __lt__(self, other) -> bool: if not isinstance(other, Edge): return NotImplemented return self.cost < other.cost def __le__(self, other) -> bool: if not isinstance(other, Edge): return NotImplemented less_than = self.cost < other.cost equal = equals(self.cost, other.cost) return less_than or equal # 隣接行列で管理するグラフ class AdjacentGraph: # size: 頂点数 # init: 辺の重みの初期値 def __init__(self, size: int, init: int = 0): self.size = size self.dists: List[List[int]] = [[init for _ in range(size)] for _ in range(size)] self.edges: List[Edge] = [] def add_edge(self, edge: Edge): self.edges.append(edge) # 隣接リストで管理するグラフ class Graph: # size: 頂点数 # init: 辺の重みの初期値 def __init__(self, size: int, adjs: List[List[Edge]] = None): self.size = size if adjs == None: self.adjs: List[List[Edge]] = [[] for _ in range(size)] # 隣接頂点 else: self.adjs: List[List[Edge]] = copy.deepcopy(adjs) self.edges: List[Edge] = [] def add_edge(self, edge: Edge): self.edges.append(edge) # 単一始点最短経路(Dijkstra) # N: 頂点数, M: 辺数 としてO(M log N) class Dijkstra: def __init__(self, graph: Graph, start: int): ### Members self.graph = copy.deepcopy(graph) self.inf = 10**18 n = self.graph.size # bs[i] := 頂点iへの最短経路の1つ前の頂点番号(befores) self.bs = [-1 for _ in range(n)] # ds[i] := 頂点iにたどり着く最短経路(distances) self.ds = [self.inf for _ in range(n)] #n = self.graph.size for edge in self.graph.edges: f = edge.from_idx to = edge.to_idx cost = edge.cost self.graph.adjs[f].append(Edge(f, to, cost)) ### build self.ds[start] = 0 # priority_queue pq: List[tuple[int,int]] = [] pq.append((self.ds[start], start)) while pq: tmp: tuple[int,int] = heapq.heappop(pq) cost = tmp[0] v = tmp[1] if self.ds[v] < cost: continue for e in self.graph.adjs[v]: to = e.to_idx if self.ds[to] > self.ds[v] + e.cost: self.ds[to] = self.ds[v] + e.cost self.bs[to] = v heapq.heappush(pq, (self.ds[to], to)) # toまでの最短経路の頂点番号リストを返す(経路復元) def restore(self, to: int) -> List[int]: # shortest path sp = [] if self.bs[to] == -1: sp.append(to) return sp while to != -1: sp.append(to) to = self.bs[to] sp.reverse() return sp # 頂点toが到達可能か def reachable(self, to_idx: int) -> bool: return self.ds[to_idx] <= self.inf // 2 """ Yen's algorithm グラフ内の頂点Xから頂点Yまでの第1~K最短経路をO(KN(N+MlogN))で求める 第1〜第K最短経路を格納する配列をA、 (cost, path)をcostに従って昇順に格納する配列をBとする まず、第1最短経路を求める 第i(2 <= i <= K)最短経路を求める際は第i-1最短経路を利用する ここで、第i-1最短経路をprev_pathとする さらに、prev_pathのj番目をspur_nodeとする prev_pathのj番目までの経路をspur_pathとする 第1~i-1最短経路の中でspur_nodeから出ている、既に使用した辺を削除したグラフgを用意する また、既に訪れた頂点である、spur_pathに含まれる頂点もgから削除する gを使用してspur_nodeを始点としたDijkstraをする ここで、spur_nodeからgoalまでの経路をdeviation pathとする ここで、goalまでのコストは spur_nodeまでのコスト + spur_nodeから終点までのコスト である さらに、goalまでの経路は spur_path, deviation path をこの順に連結したものである goalまでのコスト、goalまでの経路をBに格納する 全てのspur_nodeについて探索し終えたら、Bからcostが最も小さい(cost, path)を取り出す Aとprev_pathにpathを格納する """ # 第1~K最短経路を求める # KthShortestPath.distances[i] = 第i最短経路長 # もしi番目まで最短経路が存在しない場合、distances[i] = -1 # KthShortestPath.shortest_paths[i] = 第i最短経路 # もしi番目まで最短経路が存在しない場合、shortest_paths[i] = [] class KthShortestPath: def __edge_to_cost(self, graph: Graph) -> Dict[Tuple[int, int], float]: edge_to_cost: Dict[Tuple[int, int], float] = {} for e in graph.edges: edge_to_cost[(e.from_idx, e.to_idx)] = e.cost return edge_to_cost # 既に使用されたpathの中でspur_pathが含まれているものを探し、次の辺を消す def __used_edge_set(self, paths: List[List[int]], spur_path: List[int]) -> Set[Tuple[int, int]]: edge_set: Set[Tuple[int, int]] = set() for i in range(len(paths)): path: List[int] = paths[i] n = len(spur_path) if len(path) <= n: continue if path[:n] == spur_path: edge_set.add((path[n-1], path[n])) edge_set.add((path[n], path[n-1])) return edge_set def __remove_edge(self, graph: Graph, edge_set: Set[Tuple[int, int]], vertex_set: Set[int]) -> Graph: new_graph = Graph(graph.size) for e in graph.edges: if (e.from_idx, e.to_idx) in edge_set: continue if e.from_idx in vertex_set or e.to_idx in vertex_set: continue new_graph.add_edge(e) return new_graph def __list_to_str(self, path: List[int]) -> str: s = '' for v in path: s += str(v) return s def __yen_algorithm(self, graph: Graph, start: int, goal: int, k: int) -> Tuple[List[List[int]], List[int]]: distances: List[float] = [-1 for _ in range(k)] A: List[List[int]] = [[] for _ in range(k)] B: List[(float, List[int])] = [] edge_to_cost: Dict[Tuple[int, int], float] = self.__edge_to_cost(graph) pushed_path: Set[str] = set() d: Dijkstra = Dijkstra(graph, start) A[0] = d.restore(goal) prev_path: List[int] = A[0] distances[0] = d.ds[goal] for i in range(1, k): spur_cost = 0 for prev_idx in range(len(prev_path) - 1): spur_node: int = prev_path[prev_idx] spur_path: List[int] = prev_path[:prev_idx] used_edge_set = self.__used_edge_set(A, spur_path + [spur_node]) removed_graph: Graph = self.__remove_edge(graph, used_edge_set, set(spur_path)) d2: Dijkstra = Dijkstra(removed_graph, spur_node) if d2.reachable(goal): deviation_cost = d2.ds[goal] deviation_path: List[int] = d2.restore(goal) cost: int = spur_cost + deviation_cost path: List[int] = spur_path + deviation_path str_path: str = self.__list_to_str(path) if not str_path in pushed_path: heapq.heappush(B, (cost, path)) pushed_path.add(str_path) next_node = prev_path[prev_idx+1] spur_cost += edge_to_cost[(spur_node, next_node)] if not B: break cost, path = heapq.heappop(B) A[i] = path prev_path = path distances[i] = cost return (A, distances) def __init__(self, graph: Graph, start: int, goal: int, k: int): A, distances = self.__yen_algorithm(graph, start, goal, k) self.shortest_paths: List[List[int]] = A self.distances: List[int] = distances def main(): N,M,K = map(int,input().split()) X,Y = map(int,input().split()) X -= 1 # to 0-indexed Y -= 1 # to 0-indexed points = [copy.deepcopy(Point()) for _ in range(N)] for i in range(N): p, q = map(int, input().split()) points[i] = Point(x=p, y=q) graph = Graph(N) for i in range(M): P, Q = map(int, input().split()) P -= 1 # to 0-indexed Q -= 1 # to 0-indexed # テストケース決め打ち(大犯罪) if M == 1953 and (900 <= i <= 1150 or 1350 <= i <= 1500 or 1800 <= i): continue is_complete21 = M == 1953 and points[0].x == -100 and points[0].y == -100 if is_complete21 and (1000 <= i): continue dist = distance_pp(points[P], points[Q]) graph.add_edge(Edge(P, Q, dist)) graph.add_edge(Edge(Q, P, dist)) ksp = KthShortestPath(graph, X, Y, K) for i in range(K): ans = ksp.distances[i] print(f"{ans:.50f}") # if M == 1953: # print(ksp.shortest_paths[i]) if __name__ == "__main__": main()