#from matplotlib import pyplot as plt def visualize(N,M,pos,center_pos,allocate): fig = plt.figure(figsize=(10,10)) for i in range(N): plt.plot(pos[i][0],pos[i][1],color=color_ls[allocate[i]],marker=".") for i in range(M): plt.plot(center_pos[i][0],center_pos[i][1],color=color_ls[i],marker="*") plt.show() plt.close() INF=10**20 alpha = 5 import random from pathlib import Path import time LOCAL = False in_path = "./test" img_path = "./image" color_ls = ["red","blue","green","orange","gray","pink","cyan","black"] 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 #中心点と各惑星の距離を全探索し、短い順に並びかえる def calc_dist_from_center(N,M,center_pos,pos): dist_list = [] for m in range(M): for n in range(N): d = dist(pos[n],center_pos[m]) dist_list.append((d,m,n)) return sorted(dist_list) #中心点から近い惑星順にクラスわけする def allocate_cluster(N,M,center_pos,pos): dist_list = calc_dist_from_center(N,M,center_pos,pos) allocate = [-1]*N cluster_counts = [0]*M total_count = 0 for d,m,n in dist_list: if allocate[n]==-1: allocate[n]=m cluster_counts[m]+=1 total_count+=1 if total_count==N: break return allocate,cluster_counts #クラスター分けされた点をもとに、k-means法に倣って中心点を算出する。 # グループカウントが多いものは中心点を2つにして、クラスターを分ける。 def calc_center(N,M,pos,allocate,cluster_counts,max_size = 15,split_mode = True): point_sums = [[0,0] for _ in range(M)] for i in range(N): m=allocate[i] point_sums[m][0]+=pos[i][0] point_sums[m][1]+=pos[i][1] center_pos = [] #clsuter_countが大きい順に見ていき、中心点がM個を超えたら中断する if split_mode: order = sorted([(cluster_counts[i],i) for i in range(M)],reverse=True) else: order = [(cluster_counts[i],i) for i in range(M)] for _,m in order: if cluster_counts[m]==0: center_pos.append([random.randint(0,1000),random.randint(0,1000)]) else: center_pos.append([point_sums[m][0]//cluster_counts[m],point_sums[m][1]//cluster_counts[m]]) if cluster_counts[m]>=max_size and split_mode: x = point_sums[m][0]//cluster_counts[m] y = point_sums[m][1]//cluster_counts[m] dx = random.randint(-20,20) dy = random.randint(-20,20) while not (0<=x+dx<=1000 and 0=7 and max(cluster_counts)<=max_size: break loop+=1 center_pos = calc_center(N,M,pos,allocate,cluster_counts,split_mode=False) cluster_dist = calc_clustering_distance(center_pos,allocate,pos) #visualize(N,M,pos,center_pos,allocate) return center_pos,allocate,cluster_dist def f(S,x,n): return S*(n+1)+x #クラスター内にてBitDPでTSPを解く O(n^2*2^n) def tsp(id_list,pos,center_pos): #point 0~n-1が惑星、nが中心点 n=len(id_list)-1 dp = [INF]*(n+1)*(1<>t)&1: continue S2 = S|(1<= d - e: if t!=n: route.append(id_list[t]) else: path_list.append(route) route = [] now = t v -= d found = True break else: state = state ^ (1<>t)&1: continue d = dist(pos[id_list[now]],pos[id_list[t]]) else: d = dist(pos[id_list[now]],center_pos,a=alpha) if v - dp[f(state,t,n)] >= d - e: if t!=n: route.append(id_list[t]) else: path_list.append(route) route = [] now = t v -= d found=True break return path_list def tsp_between_space(M,center_pos): n = M dp = [[INF for _ in range(n)] for _ in range(1<>t)&1: continue S2 = S|(1<>t)&1: continue if dp[state][t]==INF: continue d = dist(center_pos[s],center_pos[t],a=1) if v - dp[state][t] >= d - e: path_list.append(t) s = t v -= d break return path_list def adjust_center_pos(path,pos,M): L=len(path) 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] center_pos = [[edge_x_sum[m]//edge_count[m],edge_y_sum[m]//edge_count[m]] for m in range(M)] return center_pos def calc_score(path,pos,center_pos): score = 0 L = len(path) cost_detail = {1:0,5:0,25:0} for i in range(L-1): a=1 if path[i]>=0: p1 = pos[path[i]] a*=5 else: p1 = center_pos[-path[i]-1] if path[i+1]>=0: p2 = pos[path[i+1]] a*=5 else: p2 = center_pos[-path[i+1]-1] score += dist(p1,p2,a) cost_detail[a]+=dist(p1,p2,a) if LOCAL: print(cost_detail) return 10**9 / (1000 + score**0.5) #クラスター内の経路について、前クラスタと最も近い接続点を一番前、後クラスタと最も近い点を一番後ろに持ってくる def reorder_path(cluster_path,m,pos,prev_pos,next_pos): prev_pos_min,next_pos_min = INF,INF prev_best,next_best = [],[] for l in cluster_path: d = dist(prev_pos,pos[l[0]]) if dbest_score: best_score = score best_pos = center_pos best_ans = ans output(best_pos,best_ans)