結果
問題 |
No.5007 Steiner Space Travel
|
ユーザー |
|
提出日時 | 2025-04-20 18:29:42 |
言語 | PyPy3 (7.3.15) |
結果 |
TLE
|
実行時間 | - |
コード長 | 7,967 bytes |
コンパイル時間 | 452 ms |
コンパイル使用メモリ | 82,484 KB |
実行使用メモリ | 108,652 KB |
スコア | 0 |
最終ジャッジ日時 | 2025-04-20 18:29:48 |
合計ジャッジ時間 | 5,357 ms |
ジャッジサーバーID (参考情報) |
judge4 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | TLE * 1 -- * 29 |
ソースコード
import copy import random from time import perf_counter import argparse import sys import math class TimeKeeper: def __init__(self): self.start_time = perf_counter() def is_time_over(self, LIMIT): return (perf_counter() - self.start_time) >= LIMIT def time_now(self): return (perf_counter() - self.start_time) def main(DEBUG): # ユークリッド距離 def euclidean_distance(p1, p2): return sum((a - b) ** 2 for a, b in zip(p1, p2)) ** 0.5 # K-meansクラスタリング def initialize_centroids(points, k): return random.sample(points, k) def assign_clusters(points, centroids): clusters = [] for p in points: ds = [euclidean_distance(p, c) for c in centroids] clusters.append(ds.index(min(ds))) return clusters def update_centroids(points, clusters, k): new_centroids = [] for i in range(k): pts = [points[j] for j in range(len(points)) if clusters[j] == i] if pts: avg = [sum(dim)/len(pts) for dim in zip(*pts)] new_centroids.append(tuple(avg)) return new_centroids def kmeans(points, k, max_iters=100): centroids = initialize_centroids(points, k) for _ in range(max_iters): clusters = assign_clusters(points, centroids) new_centroids = update_centroids(points, clusters, k) if centroids == new_centroids: break centroids = new_centroids return clusters, centroids # Held–Karp TSP def HeldKarp(dists): N = len(dists) dp = [[10**18]*N for _ in range(1<<N)] for i in range(N): dp[1<<i][i] = dists[0][i] for mask in range(1, 1<<N): for v in range(N): if not (mask & (1<<v)): continue for nv in range(N): if (mask & (1<<nv)) or dists[v][nv] < 0: continue dp[mask|(1<<nv)][nv] = min( dp[mask|(1<<nv)][nv], dp[mask][v] + dists[v][nv] ) tot = dp[(1<<N)-1][0] tour = [0] mask = (1<<N)-1 rem = tot EPS = 1e-5 cur = 0 for _ in range(N-1): mask ^= (1<<cur) for prev in range(N): if prev == cur: continue if abs(rem - dp[mask][prev] - dists[prev][cur]) < EPS: rem -= dists[prev][cur] cur = prev tour.append(cur) break return tot, tour # 移動コストの比較 def point_edge_decision(p1, p2, station, ALPHA): d12 = euclidean_distance(p1, p2) d1s = euclidean_distance(p1, station) d2s = euclidean_distance(p2, station) direct = ALPHA**2 * d12 via = ALPHA * (d1s + d2s) return (direct, False) if direct <= via else (via, True) # スコア計算 def calscore(stations, tour, ALPHA): s = 0 for (t0,i0),(t1,i1) in zip(tour, tour[1:]): i0 -= 1; i1 -= 1 if t0 == 1: x0,y0 = id_to_xy[i0] else: x0,y0 = stations[i0] if t1 == 1: x1,y1 = id_to_xy[i1] else: x1,y1 = stations[i1] d2 = (x0-x1)**2 + (y0-y1)**2 coef = {2:ALPHA**2, 3:ALPHA, 4:1}[t0+t1] s += d2 * coef return int(10**9 / (1000 + math.sqrt(s)) + 0.5) # ツアー構築部を共通化 (固定 labels, stations) def build_tour_with_fixed(labels, stations): # 各クラスタ点群と座標辞書 ps = [[] for _ in range(M)] for i,(x,y) in enumerate(points): ps[labels[i]].append((x,y,i)) id_to_xy[i] = (x,y) # グループ間TSP dists = [[euclidean_distance(stations[i], stations[j]) for j in range(M)] for i in range(M)] _, tour_grp = HeldKarp(dists) tour = [(1,1)] # 点0 を除外 ps[0].remove((id_to_xy[0][0], id_to_xy[0][1], 0)) for grp in tour_grp: station = stations[grp] tour.append((2, grp+1)) current = station rem = ps[grp].copy() while rem: best = None best_cost = float('inf') best_via = False for x,y,pid in rem: cost,via = point_edge_decision(current, (x,y), station, ALPHA) if cost < best_cost: best_cost, best = cost, (x,y,pid) best_via = via # 経由判定 if best_via and current != station: tour.append((2, grp+1)) current = station x,y,pid = best tour.append((1, pid+1)) current = (x,y) rem.remove(best) if current != station: tour.append((2, grp+1)) tour.append((2,1)) tour.append((1,1)) return tour, calscore(stations, tour, ALPHA) # solve(): k-means → 回転 → build_tour_with_fixed def solve(N, M, ALPHA, points): # k-means while True: labels, cents = kmeans(points, M) stations = [] for x,y in cents: stations.append((int(x+0.5), int(y+0.5))) if len(stations) == M: break # スタート回転 st0 = labels[0] labels = [ (c-st0)%M for c in labels ] stations = stations[st0:] + stations[:st0] # ツアー構築&スコア tour, sc = build_tour_with_fixed(labels, stations) return labels, stations, tour, sc # ステーション微調整 def refine_stations(labels, points, stations, ALPHA): best_st = stations[:] _, tour0, best_sc = None, None, -1 # 初期評価 tour0, best_sc = build_tour_with_fixed(labels, best_st) improved = True deltas = [(-1,0),(1,0),(0,-1),(0,1)] while improved: improved = False for i in range(len(best_st)): sx, sy = best_st[i] for dx, dy in deltas: cand = best_st[:] cand[i] = (sx+dx, sy+dy) tour_c, sc_c = build_tour_with_fixed(labels, cand) if sc_c > best_sc: best_sc = sc_c tour0 = tour_c best_st = cand improved = True break if improved: break return best_st, tour0, best_sc # ------------ main loop ------------ tk = TimeKeeper() LIMIT = 0.8 random.seed(0) ALPHA = 5 # 入力 N, M = map(int, input().split()) points = [tuple(map(int, input().split())) for _ in range(N)] # グローバル辞書初期化 id_to_xy = {} best_sc = -1 best_labels = best_stations = best_tour = None loop = 0 while not tk.is_time_over(LIMIT): labels, stations, tour, sc = solve(N, M, ALPHA, points) if sc > best_sc: best_sc = sc best_labels, best_stations, best_tour = labels, stations, tour loop += 1 # ステーション微調整 best_stations, best_tour, best_sc = refine_stations( best_labels, points, best_stations, ALPHA ) # 出力 print('SC', best_sc, file=sys.stderr) for x,y in best_stations: print(x, y) print(len(best_tour)) for t,i in best_tour: print(t, i) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Debug mode') parser.add_argument('--debug', action='store_true', help='Enable debug mode') args = parser.parse_args() main(args.debug)