結果
| 問題 |
No.1069 電柱 / Pole (Hard)
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:03:55 |
| 言語 | PyPy3 (7.3.15) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 8,889 bytes |
| コンパイル時間 | 212 ms |
| コンパイル使用メモリ | 82,884 KB |
| 実行使用メモリ | 89,284 KB |
| 最終ジャッジ日時 | 2025-05-14 13:05:48 |
| 合計ジャッジ時間 | 10,696 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 78 WA * 1 |
ソースコード
import math
import heapq
import sys
# Use sys.stdin.readline for potentially faster input reading
input = sys.stdin.readline
def solve():
# Read N (number of poles), M (number of wire candidates), K (number of shortest paths required)
N, M, K = map(int, input().split())
# Read X (start pole index), Y (end pole index)
X, Y = map(int, input().split())
# Adjust indices to be 0-based for list access
X -= 1
Y -= 1
# Read coordinates for each pole
coords = []
for i in range(N):
coords.append(list(map(int, input().split())))
# Build adjacency list representation of the graph
# adj dictionary maps a pole index u to a list of tuples (v, distance),
# where v is a neighbor and distance is the Euclidean distance between u and v.
adj = {}
for _ in range(M):
# Read the two poles connected by a wire candidate
u, v = map(int, input().split())
u -= 1 # Adjust to 0-based index
v -= 1 # Adjust to 0-based index
# Get coordinates of poles u and v
p1 = coords[u]
p2 = coords[v]
# Calculate Euclidean distance using math.dist for better precision and clarity
dist = math.dist(p1, p2)
# Add the edge to the adjacency list for both directions since the connection is bidirectional
if u not in adj: adj[u] = []
if v not in adj: adj[v] = []
adj[u].append((v, dist))
adj[v].append((u, dist))
# State storage using a list of dictionaries. Each dictionary represents a state in the search.
# The index in this list acts as a unique state ID.
# Each state: {'cost': float (g), 'node': int (u), 'parent_idx': int}
# 'cost' is the actual path cost from X to 'node'.
# 'parent_idx' points to the state ID from which this state was reached, allowing path reconstruction.
states = []
# Add the initial state: starting at node X with cost 0.0 and no parent (-1).
initial_state = {'cost': 0.0, 'node': X, 'parent_idx': -1}
states.append(initial_state)
state_id_counter = 1 # Counter for assigning unique IDs to new states
# Heuristic function h(u): estimates the cost from node u to the target Y.
# Using Euclidean distance (straight-line distance) as it is admissible for shortest path problems on a 2D plane.
# Admissible means h(u) never overestimates the actual shortest path cost from u to Y.
def heuristic(u):
# math.dist calculates the Euclidean distance between two points.
return math.dist(coords[u], coords[Y])
# Priority queue (min-heap) for A* search. Stores tuples:
# (f_cost, path_cost_g, current_node_u, state_id)
# f_cost = path_cost_g + heuristic(current_node_u). The priority queue orders states by f_cost.
pq = []
# Calculate the initial f_cost for the starting node X (g=0.0).
initial_f_cost = 0.0 + heuristic(X)
# Push the initial state onto the priority queue.
heapq.heappush(pq, (initial_f_cost, 0.0, X, 0))
# Dictionary to keep track of how many times a path ending at node 'u' has been finalized (popped from PQ).
# This is used for pruning the search based on K: we only need the K shortest paths.
dist_count = {} # node -> count. Initialized lazily.
results = [] # List to store the costs (g values) of paths found that reach the target Y.
# Main A* search loop: continues as long as the priority queue is not empty
# and we have not yet found K paths to the target Y.
while pq and len(results) < K:
# Pop the state with the lowest estimated total cost (f_cost) from the priority queue.
f, g, u, state_idx = heapq.heappop(pq)
# Goal Check: If the current node u is the target Y.
if u == Y:
results.append(g) # Store the actual path cost g.
# Found one path to Y. Continue the search as we need up to K paths.
continue
# K-Pruning: Check how many paths ending at node u have already been finalized.
current_count = dist_count.get(u, 0) # Get current count, default 0 if not seen before.
if current_count >= K:
# If K paths reaching/passing through node u have already been processed and finalized,
# we can skip exploring further from this state. This significantly reduces the search space,
# especially crucial for small K values.
continue
# Increment the count of finalized paths for node u.
dist_count[u] = current_count + 1
# Path Reconstruction for Simplicity Check:
# Reconstruct the path nodes from the start node X to the current node u
# by following the parent pointers stored in the states list.
# This is necessary to check if adding a neighbor would create a cycle (violate path simplicity).
visited_nodes = set()
curr_state_idx = state_idx
path_reconstruction_valid = True # Safety flag for reconstruction process.
# Iteratively trace back parent pointers until the initial state (parent_idx = -1) is reached.
while curr_state_idx != -1:
# Basic bounds check for robustness, ensuring state index is valid.
if curr_state_idx < 0 or curr_state_idx >= len(states):
path_reconstruction_valid = False
# Optionally log an error or handle this unexpected situation.
# print(f"Error: Invalid state index {curr_state_idx} during path reconstruction.")
break
state = states[curr_state_idx] # Get the state information using its ID.
node_in_path = state['node'] # Get the node associated with this state.
# Add the node to the set of visited nodes for the current path being reconstructed.
visited_nodes.add(node_in_path)
# Move up to the parent state using the stored parent index.
curr_state_idx = state['parent_idx']
# If path reconstruction failed (e.g., due to invalid index), skip processing this state.
if not path_reconstruction_valid: continue
# The visited_nodes set now contains all unique nodes on the simple path from X to u for this state.
# Explore Neighbors: Iterate through neighbors v of the current node u.
if u not in adj: # Check if node u has any outgoing edges defined in adj list.
continue # If no neighbors, nothing to explore from here.
for v, weight in adj[u]: # For each neighbor v connected by an edge with cost 'weight'.
# Path Simplicity Check: Ensure the neighbor 'v' has NOT been visited already on the current path.
# This check prevents cycles and ensures that the paths found are simple.
if v not in visited_nodes:
# Calculate the new path cost (g_new) by adding the edge weight.
g_new = g + weight
# Calculate the heuristic estimate for the neighbor node v.
h_new = heuristic(v)
# Calculate the new estimated total cost (f_new = g_new + h_new).
f_new = g_new + h_new
# Create a new state dictionary representing reaching node 'v' via 'u' from path state `state_idx`.
new_state = {'cost': g_new, 'node': v, 'parent_idx': state_idx}
# Append this new state to the global `states` list.
states.append(new_state)
# Assign the new state its unique ID using the counter.
new_state_idx = state_id_counter
# Increment the counter for the next state ID.
state_id_counter += 1
# Push the new state's information (f_new, g_new, v, new_state_idx) onto the priority queue.
heapq.heappush(pq, (f_new, g_new, v, new_state_idx))
# After the A* search loop finishes:
# Format the output results.
final_results = []
# Sort the found path costs in non-decreasing order.
results.sort()
printed_count = 0
# Select the top K path costs from the sorted results list.
for i in range(len(results)):
if printed_count < K:
# Format the cost to exactly 6 decimal places as required.
final_results.append(f"{results[i]:.6f}")
printed_count += 1
else:
# Stop once K results have been prepared.
break
# If fewer than K paths were found, fill the remaining slots in the output with "-1".
while printed_count < K:
final_results.append("-1")
printed_count += 1
# Print each result string on a new line.
print('\n'.join(final_results))
# Execute the main solve function to run the algorithm.
solve()
qwewe