結果
問題 | No.5007 Steiner Space Travel |
ユーザー | prussian_coder |
提出日時 | 2023-04-30 14:29:52 |
言語 | PyPy3 (7.3.15) |
結果 |
TLE
|
実行時間 | - |
コード長 | 15,275 bytes |
コンパイル時間 | 383 ms |
コンパイル使用メモリ | 87,436 KB |
実行使用メモリ | 94,920 KB |
スコア | 7,939,156 |
最終ジャッジ日時 | 2023-04-30 14:30:26 |
合計ジャッジ時間 | 33,380 ms |
ジャッジサーバーID (参考情報) |
judge15 / judge11 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 987 ms
91,372 KB |
testcase_01 | AC | 959 ms
92,320 KB |
testcase_02 | AC | 974 ms
92,116 KB |
testcase_03 | AC | 981 ms
91,276 KB |
testcase_04 | AC | 980 ms
91,800 KB |
testcase_05 | AC | 976 ms
92,216 KB |
testcase_06 | AC | 987 ms
90,948 KB |
testcase_07 | AC | 992 ms
92,732 KB |
testcase_08 | AC | 985 ms
92,596 KB |
testcase_09 | AC | 982 ms
91,428 KB |
testcase_10 | AC | 979 ms
90,896 KB |
testcase_11 | AC | 981 ms
92,660 KB |
testcase_12 | AC | 965 ms
90,700 KB |
testcase_13 | AC | 970 ms
91,288 KB |
testcase_14 | AC | 996 ms
91,844 KB |
testcase_15 | AC | 969 ms
90,052 KB |
testcase_16 | AC | 984 ms
92,084 KB |
testcase_17 | AC | 967 ms
91,592 KB |
testcase_18 | TLE | - |
testcase_19 | AC | 968 ms
91,932 KB |
testcase_20 | AC | 970 ms
93,092 KB |
testcase_21 | AC | 963 ms
90,944 KB |
testcase_22 | AC | 990 ms
90,976 KB |
testcase_23 | AC | 983 ms
91,188 KB |
testcase_24 | TLE | - |
testcase_25 | AC | 977 ms
90,484 KB |
testcase_26 | AC | 957 ms
90,392 KB |
testcase_27 | AC | 968 ms
90,116 KB |
testcase_28 | AC | 981 ms
90,940 KB |
testcase_29 | AC | 993 ms
92,508 KB |
ソースコード
import random from pathlib import Path import time import os import math LOCAL = False in_path = "./test" out_path = "./test/result" INF=10**20 alpha = 5 FILE_OUTPUT = True def read_data(file): if LOCAL: with open(file,mode="r") as f: data = f.readlines() N,M = map(int,data[0].split()) pos = [[int(x) for x in data[i+1].split()] for i in range(N)] else: N,M=map(int,input().split()) pos = [[int(x) for x in input().split()] for i in range(N)] return N,M,pos #2点間の距離を返す def dist(p1,p2,a=alpha**2): return ((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) * a # 一番近い惑星を貪欲に選び続ける(Nearest Neighbour法) def nearest_neighbor(pos,N): best_route = [] best_cost = INF for _ in range(10): cost = 0 start_point = random.randint(0,N-1) #初期位置はランダム v = start_point visited = [False] * N visited[v] = True route = [v] # 初期惑星以外のN-1個の惑星を訪問していく for _ in range(N - 1): nearest_dist = INF nearest_v = -1 # 一番近い惑星を探す for next in range(N): if visited[next]: continue d = dist(pos[v], pos[next]) if d < nearest_dist: nearest_dist = d nearest_v = next # 次の頂点に移動 v = nearest_v visited[v] = True route.append(nearest_v) cost += nearest_dist cost += dist(pos[next],pos[start_point]) if cost<best_cost: best_cost=cost best_route=route return best_route #焼きなまし関数 def simulated_annealing(score,temp): if score<=0: return True elif score/temp > 10: return False return math.exp(-score/temp) > random.random() #2-opt近傍 def two_opt(state,temp): #print("two-opt") if random.random()<0.4: i = random.randint(1,state.N-2) j = random.randint(1,state.N-2) else: i = random.randint(1,state.N-2) j = (i + random.randint(2,10))%(state.N-1) if i>j: i,j=j,i #(a,b)-(c,d)->(a,c)-(b-d) a = state.route[i] b = state.route[i+1] c = state.route[j] d = state.route[j+1] if len(set({a,b,c,d}))!=4: return False ab_cd = state.calc_distance(a,b) + state.calc_distance(c,d) ac_bd = state.calc_distance(a,c) + state.calc_distance(b,d) if simulated_annealing(ac_bd-ab_cd,temp): state.route[i+1:j+1] = state.route[i+1:j+1][::-1] state.cost += ac_bd-ab_cd return True else: return False #挿入近傍 def point_insert(state,temp): #print("Point_insert") i = random.randint(1,state.N-2) j = random.randint(1,state.N-2) if i>j: i,j=j,i #(a,b,c)-(d,e)->(a,c)-(d,b,e) a = state.route[i-1] b = state.route[i] c = state.route[i+1] d = state.route[j] e = state.route[j+1] if len(set({a,b,c,d,e}))!=5: return False abc_de = state.calc_distance(a,b) + state.calc_distance(b,c) + state.calc_distance(d,e) ac_dbe = state.calc_distance(a,c) + state.calc_distance(d,b) + state.calc_distance(b,e) if simulated_annealing(ac_dbe-abc_de,temp): state.route.remove(b) if i<j: state.route.insert(j,b) else: state.route.insert(j+1,b) state.cost += ac_dbe-abc_de return True else: return False #3-opt def three_opt(state,temp): #print("three-opt") i = random.randint(1,state.N-2) j = random.randint(1,state.N-2) if random.random()<0.4: k = (j + random.randint(2,4))%state.N #or-opt like else: k = random.randint(1,state.N-2) i,j,k = sorted((i,j,k)) if abs(i-j)<=1 or abs(j-k)<=1: return False cost_list = [] a, b, c, d, e, f = state.route[i - 1], state.route[i], state.route[j - 1], state.route[j], state.route[k - 1], state.route[k % len(state.route)] current_cost = state.calc_distance(a,b) + state.calc_distance(c,d) + state.calc_distance(e,f) for mode in range(8): A,B,C,D,E,F = a,b,c,d,e,f if (mode>>2)&1: B,C=C,B if (mode>>1)&1: D,E=E,D if mode&1: F,A=A,F new_cost = state.calc_distance(A,B) + state.calc_distance(C,D) + state.calc_distance(E,F) cost_list.append((new_cost-current_cost,mode)) cost_list.sort() if cost_list[0][1]!=0: next_mode = cost_list[0][1] state.cost += cost_list[0][0] elif simulated_annealing(cost_list[1][0],temp): next_mode = cost_list[1][1] state.cost += cost_list[1][0] else: return False if (i - 1) < (k % len(state.route)): first_segment = state.route[k% len(state.route):] + state.route[:i] else: first_segment = state.route[k % len(state.route):i] second_segment = state.route[i:j] third_segment = state.route[j:k] if next_mode&1: first_segment.reverse() if (next_mode>>1)&1: third_segment.reverse() if (next_mode>>2)&1: second_segment.reverse() state.route = first_segment + second_segment + third_segment return True #宇宙船の位置を少し動かす def move_station(state,temp): move_range = 30 m = random.randint(0,state.M-1) x,y = state.center_pos[m] dx = random.randint(-move_range,move_range) dy = random.randint(-move_range,move_range) current_cost = state.cost x_new = max(0,min(1000,x+dx)) y_new = max(0,min(1000,y+dy)) state.center_pos[m] = [x_new,y_new] new_cost = state.calc_cost() if simulated_annealing(new_cost-current_cost,temp): return True else: state.center_pos[m]=[x,y] #宇宙ステーションを別の場所に移動させる def replace_station(state,temp): point_ls = list(find_edge_point(state,2)) ini_x = (state.pos[point_ls[0]][0]+state.pos[point_ls[1]][0])//2 ini_y = (state.pos[point_ls[0]][1]+state.pos[point_ls[1]][1])//2 ini_point = [ini_x,ini_y] surrounding = [] for p in range(state.N): if dist(ini_point,state.pos[p],a=1)<=100: surrounding.append(p) if not surrounding: return False new_pos = [sum(state.pos[p][s] for p in surrounding)//len(surrounding) for s in range(2)] current_cost = state.calc_cost() m = random.randint(0,state.M-1) prev_pos = state.center_pos[m] state.center_pos[m]= new_pos state.find_nearest_station() new_cost = state.calc_cost() if simulated_annealing(new_cost-current_cost,temp): return True else: state.center_pos[m]=prev_pos state.find_nearest_station() new_cost = state.calc_cost() return False def relocate_by_kmeans(state,temp): current_cost = state.calc_cost() current_pos = state.center_pos.copy() K_means_clustering(state) state.find_nearest_station() new_cost = state.calc_cost() if simulated_annealing(new_cost-current_cost,temp): return True else: state.center_pos=current_pos state.find_nearest_station() new_cost = state.calc_cost() return False def adjust_station(state,temp): path = state.ans() L=len(path) M=state.M edge_count = [0]*M edge_x_sum = [0]*M edge_y_sum = [0]*M for i in range(L-1): if path[i]<0 and path[i+1]>=0: p,q = -path[i]-1,path[i+1] edge_count[p]+=5 edge_x_sum[p]+=pos[q][0]*5 edge_y_sum[p]+=pos[q][1]*5 elif path[i]>=0 and path[i+1]<0: p,q = -path[i+1]-1,path[i] edge_count[p]+=5 edge_x_sum[p]+=pos[q][0]*5 edge_y_sum[p]+=pos[q][1]*5 elif path[i]<0 and path[i+1]<0: p,q = -path[i]-1,-path[i+1]-1 edge_count[p]+=1 edge_count[q]+=1 edge_x_sum[p]+=pos[q][0] edge_y_sum[p]+=pos[q][1] edge_x_sum[q]+=pos[p][0] edge_y_sum[q]+=pos[p][1] for m in range(M): if edge_count[m]==0: state.center_pos[m] = [random.randint(0,1000),random.randint(0,1000)] else: state.center_pos[m] = [edge_x_sum[m]//edge_count[m],edge_y_sum[m]//edge_count[m]] state.cost = state.calc_cost() return True #焼きなましで経路を短くする def optimize_route(state,mode_ls,time_limit,start_temp,end_temp): dt=time.time() trial = dict() success = dict() for mode in mode_ls: trial[mode[0]]=0 success[mode[0]]=0 while time.time()-dt<time_limit: temp = start_temp + (end_temp - start_temp) * (time.time()-dt)/time_limit rand = random.random() for mode in mode_ls: if rand<mode[1]: trial[mode[0]]+=1 flag = mode[0](state,temp) if flag: success[mode[0]]+=1 break #print(trial,success) #隣接する惑星間の距離が長い惑星を出力する O(N) # limit: 出力する個数 def find_edge_point(state,limit): edge_list = [(state.calc_distance(state.route[i-1],state.route[i]),state.route[i-1],state.route[i]) for i in range(len(state.route))] edge_list.sort(reverse=True) point_ls = set() count = 0 for v,i,j in edge_list: if not i in point_ls: point_ls.add(i) count+=1 if not j in point_ls: point_ls.add(j) count+=1 if count >= limit: return point_ls def K_means_clustering(state): use_list = find_edge_point(state,30) valid_station = {0,1,2,3} for m in range(state.M): if not m in valid_station: state.center_pos[m]=[INF,INF] for _ in range(40): state.find_nearest_station() state.set_station(use_list,valid_station) use_list = find_edge_point(state,30) valid_station = {4,5,6,7} for _ in range(40): state.find_nearest_station() state.set_station(use_list,valid_station) return class State: def __init__(self,N,M,pos,initial_route): self.N=N self.M=M self.route = initial_route(pos,N) self.allocate = [-1]*N self.pos = pos self.center_pos = [[random.randint(0,1000),random.randint(0,1000)] for _ in range(M)] self.cost = self.calc_cost() #惑星iと惑星jの移動について、宇宙ステーションを用いた際の最適な距離とルートを返す def calc_distance(self,i,j,output_path = False): path_candidate = [] #(distance,connector) #p1→p2 d = dist(self.pos[i],self.pos[j]) path_candidate.append((d,[])) #p1→m1→p2 if self.allocate[i]!=-1: m1 = self.allocate[i] d = dist(self.pos[i],self.center_pos[m1],a=alpha) + dist(self.pos[j],self.center_pos[m1],a=alpha) path_candidate.append((d,[-m1-1])) #p1→m2→p2 if self.allocate[j]!=-1: m2 = self.allocate[j] d = dist(self.pos[i],self.center_pos[m2],a=alpha) + dist(self.pos[j],self.center_pos[m2],a=alpha) path_candidate.append((d,[-m2-1])) #p1→m1→→m2→p2 if self.allocate[i]!=-1 and self.allocate[j]!=-1: m1 = self.allocate[i] m2 = self.allocate[j] d = dist(self.pos[i],self.center_pos[m1],a=alpha) + dist(self.pos[j],self.center_pos[m2],a=alpha) + dist(self.center_pos[m1],self.center_pos[m2],a=1) path_candidate.append((d,[-m1-1,-m2-1])) path_candidate.sort() if output_path: return path_candidate[0][1] else: return path_candidate[0][0] #1周のコストを計算する def calc_cost(self): cost = 0 for i in range(len(self.route)): cost += self.calc_distance(self.route[i-1],self.route[i]) return cost def ans(self): res = [] start_point = 0 p1 = start_point v1 = self.route.index(start_point) for _ in range(self.N): res.append(p1) v2 = (v1+1)%self.N p2 = self.route[v2] connector = self.calc_distance(p1,p2,output_path=True) for c in connector: res.append(c) v1 = v2 p1 = p2 res.append(p1) return res def find_nearest_station(self): for i in range(self.N): d = [dist(self.pos[i],self.center_pos[j]) for j in range(self.M)] self.allocate[i] = d.index(min(d)) def set_station(self,use_list,valid_station): point_count = [0]*M point_pos_sum = [[0,0] for _ in range(M)] for v in use_list: m = self.allocate[v] point_count[m]+=1 point_pos_sum[m][0]+=self.pos[v][0] point_pos_sum[m][1]+=self.pos[v][1] for m in range(M): if not m in valid_station: continue if point_count[m]==0: self.center_pos[m]=[random.randint(0,1000),random.randint(0,1000)] else: self.center_pos[m]=[point_pos_sum[m][0]//point_count[m],point_pos_sum[m][1]//point_count[m]] def main(N,M,pos): state = State(N,M,pos,nearest_neighbor) mode_ls = [(two_opt,0.6),(three_opt,0.8),(point_insert,1)] optimize_route(state,mode_ls,0.15,500,100) K_means_clustering(state) mode_ls = [(adjust_station,0.005),(relocate_by_kmeans,0.01),(replace_station,0.05),(move_station,0.15),(two_opt,0.7),(three_opt,0.85),(point_insert,1)] optimize_route(state,mode_ls,0.52,500,100) adjust_station(state,0) return state.center_pos,state.ans(),state.cost def output(center_pos,ans,score,file=""): if LOCAL and FILE_OUTPUT: Path(out_path).mkdir(exist_ok=True) file_out = os.path.join(out_path,file.stem + "_" + str(int(score))+".txt") with open(file_out,mode="w") as f: for x,y in center_pos: f.write(str(x)+" "+str(y)+"\n") f.write(str(len(ans))+"\n") for a in ans: if a<0: f.write("{0} {1}\n".format(2,-a)) else: f.write("{0} {1}\n".format(1,a+1)) else: for x,y in center_pos: print(x,y) print(len(ans)) for a in ans: if a<0: print(2,-a) else: print(1,a+1) if LOCAL: file_ls = Path(in_path).glob("*.txt") for file in file_ls: print(file) N,M,pos = read_data(file) center_pos,ans,score = main(N,M,pos) output(center_pos,ans,score,file) print(score) else: N,M,pos = read_data("") best_score=0 for _ in range(1): center_pos,ans,score = main(N,M,pos) if score>best_score: best_score = score best_pos = center_pos best_ans = ans output(best_pos,best_ans,score)