#!/usr/bin/python3 from math import sqrt class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return "[ " + str(self.x) + " " + str(self.y) + " ]" # prima vzdalenost def distance_to(self, point): return sqrt( (self.x - point.x)**2 + (self.y - point.y)**2 ) # nejkratsi vzdalenost def dijkstra_to(self, target_point, neighbors): dist = { self : 0 } open_points = { self } closed_points = set() return_path = {} while open_points: # najdeme bod s nejmensi vzdalenosti min_point = None for point in open_points: if min_point is None or dist[point] < dist[min_point]: min_point = point # uzavreme a prepocitame ostatni body open_points.discard(min_point) closed_points.add(min_point) if min_point is target_point: # projdeme zpatky a najdeme nejkratsi cestu current_point = min_point path = [] while current_point is not self: path.append(current_point) current_point = return_path[current_point] path.append(self) path.reverse() return dist[min_point], path for neighbor in neighbors[min_point]: if neighbor not in closed_points: open_points.add(neighbor) our_dist = dist[min_point] + min_point.distance_to(neighbor) if neighbor not in dist or dist[neighbor] > our_dist: dist[neighbor] = our_dist return_path[neighbor] = min_point # pokud jsme se dostali sem, cesta neexistuje return None # Vektor urceny pocatecnim bodem A a smerovym B class Vector: def __init__(self, pointA, pointB): self.x = pointB.x - pointA.x self.y = pointB.y - pointA.y # Vypocte determinant dvou vektoru # viz geometricka kucharka # https://ksp.mff.cuni.cz/viz/kucharky/geometrie def determinant(vectorA, vectorB): return vectorB.x * vectorA.y - vectorB.y * vectorA.x # Vrati True, pokud se bod nachazi ve vyseci mezi dvema vektory (ve smeru hodinovych # rucicek od vektoru A) def lies_between(vectorA, vectorB, origin, point): # Predpokladame, ze vektor B lezi "napravo" od A (sviraji mensi uhel nez 180 # stupnu), jinak vektory prohodime if(determinant(vectorA, vectorB) >= 0): vectorP = Vector(Point(0,0), Point(point.x - origin.x, point.y - origin.y)) return determinant(vectorA, vectorP) >= 0 and determinant(vectorB, vectorP) <= 0 else: return not lies_between(vectorB, vectorA, origin, point) # Kroketova branka class Gate: def __init__(self, pointA, pointB): self.pointA = pointA self.pointB = pointB # orientace branky: polorovina napravo od poloprimky, jejimz pocatkem je A # a smerem B # urcime pomoci determinantu -- viz geometricka kucharka # https://ksp.mff.cuni.cz/viz/kucharky/geometrie # Je zadany bod v polorovine, do ktere ma smerovat micek? def can_reach(self, point): if self.pointA == self.pointB: return True else: line_vector = Vector(self.pointA, self.pointB) point_vector = Vector(self.pointA, point) return determinant(line_vector, point_vector) > 0 # nacitani popisu hriste # # # # ... dalsi branky ... # def read_float_list(length): nums = [ float(str_num) for str_num in input().split(' ') ] assert len(nums) == length return nums def read_input(): num_gates = int(input()) gates = [] # start a cil je branka s body na jednom miste (start_x, start_y) = read_float_list(2) start_point = Point(start_x, start_y) gates.append(Gate(start_point, start_point)) for _ in range(num_gates): (a_x, a_y, b_x, b_y) = read_float_list(4) gates.append(Gate( Point(a_x, a_y), Point(b_x, b_y))) (target_x, target_y) = read_float_list(2) end_point = Point(target_x, target_y) gates.append(Gate(end_point, end_point)) return gates # Zacne u urceneho bodu, prochazi dozpatku branky a vrati body, ze kterych se do # urceneho bodu primo dostaneme def reachable_from(gates, origin_num, origin_point): origin = None if origin_point == "A": origin = gates[origin_num].pointA else: origin = gates[origin_num].pointB # aktualni vysec je mezi min_vector a max_vector # (od min_vector ve smeru hodinovych rucicek) min_vector = Vector(gates[origin_num].pointB, gates[origin_num].pointA) max_vector = Vector(gates[origin_num].pointA, gates[origin_num].pointB) reachable = [] for num_gate in range(origin_num-1, -1, -1): this_gate = gates[num_gate] # je tato branka spravne orientovana? if not this_gate.can_reach(origin): break # dostaneme se do bodu branky? a_reachable = lies_between(min_vector, max_vector, origin, this_gate.pointA) b_reachable = lies_between(min_vector, max_vector, origin, this_gate.pointB) if a_reachable: reachable.append(this_gate.pointA) min_vector = Vector(origin, this_gate.pointA) if b_reachable: reachable.append(this_gate.pointB) max_vector = Vector(origin, this_gate.pointB) if not a_reachable and not b_reachable: # branka je uplne mimo nas uhel break return reachable gates = read_input() neighbors = {} for num_gate in range(len(gates)): this_gate = gates[num_gate] neighbors[this_gate.pointA] = [] neighbors[this_gate.pointB] = [] for reachable in reachable_from(gates, num_gate, "A"): neighbors[reachable].append(this_gate.pointA) for reachable in reachable_from(gates, num_gate, "B"): neighbors[reachable].append(this_gate.pointB) res = gates[0].pointA.dijkstra_to( gates[-1].pointA, neighbors ) if res is not None: print("delka:", res[0]) print("body:", res[1]) else: print("cesta neexistuje")