using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; class Yukico5007 { static void Main(string[] args) { var sw = Stopwatch.StartNew(); var nums = Console.ReadLine().Split(' ').Select(int.Parse).ToArray(); var N = nums[0]; var M = nums[1]; var planets = new List<(int y, int x)>(); for (int i = 0; i < N; i++) { nums = Console.ReadLine().Split(' ').Select(int.Parse).ToArray(); planets.Add((nums[0], nums[1])); } var directCosts = new double[(N + 1) * (N + 1)]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (i == j) continue; var cost = Math.Sqrt(Math.Abs(planets[i].y - planets[j].y) * Math.Abs(planets[i].y - planets[j].y) + Math.Abs(planets[i].x - planets[j].x) * Math.Abs(planets[i].x - planets[j].x)); directCosts[i * (N + 1) + j] = cost; } } for (int i = 0; i < N; i++) { directCosts[(N + 1) * N + i] = directCosts[0 * (N + 1) + i]; directCosts[(N + 1) * i + N] = directCosts[i * (N + 1) + 0]; } var inputs = new Inputs(sw, planets, directCosts, null); var solver = new Solver(); var command = solver.GetCommand(inputs); command.Print(); } } class Inputs { /// 惑星の座標 public List<(int y, int x)> Planets; /// 他の惑星を経由せずに直接飛ぶコスト(αは考慮しない) public double[] DirectCosts; /// 他の惑星を経由して飛ぶ最小コスト(αは考慮しない) public double[] RouteCosts; public Stopwatch Sw; public const int N = 100; public const int NP1 = 101; public const int M = 8; public const int A = 5; public const int AA = 25; public Inputs(Stopwatch sw, List<(int y, int x)> planets, double[] directCosts, double[] routeCosts) { Sw = sw; Planets = planets; DirectCosts = directCosts; RouteCosts = routeCosts; } } class Outputs { public List<(int y, int x)> Stations = new(); public List<(int t, int r)> Route = new(); public double Cost; public void Print() { foreach (var pos in Stations) { Console.WriteLine($"{pos.y} {pos.x}"); } Console.WriteLine(Route.Count); foreach (var via in Route) { Console.WriteLine($"{via.t} {via.r + 1}"); } } } class Solver { public Outputs GetCommand(Inputs inputs) { var defaultRoute = Enumerable.Range(0, Inputs.N).Append(0).ToArray(); var twoOpt = new TwoOpt(Inputs.NP1, inputs.DirectCosts, defaultRoute, new Random(1728), inputs.Sw); twoOpt.Exec(); var res = new Outputs(); foreach (var i in twoOpt.IdxByOrders) { res.Route.Add((1, i)); } res = AddStations(inputs, res, twoOpt.IdxByOrders); res = Yaki(inputs, res, twoOpt.IdxByOrders); return res; } int[] GetInitRoute(Inputs inputs) { var res = new int[Inputs.NP1]; var route = new Span(res); route[0] = 0; var seen = new Span(new bool[Inputs.N]); seen[0] = true; var bf = 0; for (int i = 1; i < Inputs.N; i++) { var bestIdx = -1; var bestCost = double.MaxValue; for (int j = 1; j < Inputs.N; j++) { if (seen[j]) continue; var cost = inputs.DirectCosts[bf * Inputs.NP1 + j]; if (cost < bestCost) { bestIdx = j; bestCost = cost; } } route[i] = bestIdx; bf = bestIdx; seen[bestIdx] = true; } route[Inputs.N] = 0; return res; } Outputs AddStations(Inputs inputs, Outputs outputs, int[] order) { while (outputs.Stations.Count < 8) { var maxCost = double.MinValue; var maxCostIndex = -1; // (i - 1) -> i for (int i = 1; i < outputs.Route.Count; i++) { var bf = outputs.Route[i - 1]; var af = outputs.Route[i]; if (af.t == 2 || af.t == 2) continue; double cost; if (bf.t == 1 && af.t == 1) { cost = inputs.DirectCosts[bf.r * Inputs.NP1 + af.r] * Inputs.AA; } else { var rate = (bf.t == 2 && af.t == 2) ? 1 : Inputs.A; var bfPos = bf.t == 1 ? inputs.Planets[bf.r] : outputs.Stations[bf.r]; var afPos = af.t == 1 ? inputs.Planets[af.r] : outputs.Stations[af.r]; cost = Math.Sqrt(Math.Abs(bfPos.y - afPos.y) * Math.Abs(bfPos.y - afPos.y) + Math.Abs(bfPos.x - afPos.x) * Math.Abs(bfPos.x - afPos.x)) * rate; } if (cost > maxCost) { maxCostIndex = i; maxCost = cost; } } var bf2 = outputs.Route[maxCostIndex - 1]; var af2 = outputs.Route[maxCostIndex]; var bfPos2 = bf2.t == 1 ? inputs.Planets[bf2.r] : outputs.Stations[bf2.r]; var afPos2 = af2.t == 1 ? inputs.Planets[af2.r] : outputs.Stations[af2.r]; var y = (bfPos2.y + afPos2.y) / 2; var x = (bfPos2.x + afPos2.x) / 2; outputs.Stations.Add((y, x)); outputs = GetNextRoute(inputs, outputs, order); } return outputs; } Outputs GetNextRoute(Inputs inputs, Outputs outputs, int[] order) { var res = new Outputs(); res.Stations = new List<(int y, int x)>(outputs.Stations); res.Route.Add((1, 0)); for (int i = 1; i < order.Length; i++) { var bfPlanet = order[i - 1]; var directCost = inputs.DirectCosts[bfPlanet * Inputs.NP1 + order[i]] * inputs.DirectCosts[bfPlanet * Inputs.NP1 + order[i]] * Inputs.AA; // 直接 var bestT = 1; var bestR = -1; var bestCost = directCost; var bfPos = inputs.Planets[bfPlanet]; var afPos = inputs.Planets[order[i]]; for (int j = 0; j < res.Stations.Count; j++) { var ctPos = res.Stations[j]; var cost1 = Math.Sqrt(Math.Abs(bfPos.y - ctPos.y) * Math.Abs(bfPos.y - ctPos.y) + Math.Abs(bfPos.x - ctPos.x) * Math.Abs(bfPos.x - ctPos.x)); var cost2 = Math.Sqrt(Math.Abs(afPos.y - ctPos.y) * Math.Abs(afPos.y - ctPos.y) + Math.Abs(afPos.x - ctPos.x) * Math.Abs(afPos.x - ctPos.x)); cost1 = cost1 * cost1 * Inputs.A; cost2 = cost2 * cost2 * Inputs.A; if (cost1 + cost2 < bestCost) { bestT = 2; bestR = j; bestCost = cost1 + cost2; } } if (bestR != -1) { res.Route.Add((bestT, bestR)); } res.Route.Add((1, order[i])); res.Cost += bestCost; } return res; } Outputs GetNextRoute(Inputs inputs, Outputs outputs, List<(int y, int x)> stations, int[] order) { var res = new Outputs(); res.Stations = new List<(int y, int x)>(stations); res.Route.Add((1, 0)); for (int i = 1; i < order.Length; i++) { var bfPlanet = order[i - 1]; var directCost = inputs.DirectCosts[bfPlanet * Inputs.NP1 + order[i]] * inputs.DirectCosts[bfPlanet * Inputs.NP1 + order[i]] * Inputs.AA; // 直接 var bestT = 1; var bestR = -1; var bestCost = directCost; var bfPos = inputs.Planets[bfPlanet]; var afPos = inputs.Planets[order[i]]; for (int j = 0; j < stations.Count; j++) { var ctPos = stations[j]; var cost1 = Math.Sqrt(Math.Abs(bfPos.y - ctPos.y) * Math.Abs(bfPos.y - ctPos.y) + Math.Abs(bfPos.x - ctPos.x) * Math.Abs(bfPos.x - ctPos.x)); var cost2 = Math.Sqrt(Math.Abs(afPos.y - ctPos.y) * Math.Abs(afPos.y - ctPos.y) + Math.Abs(afPos.x - ctPos.x) * Math.Abs(afPos.x - ctPos.x)); cost1 = cost1 * cost1 * Inputs.A; cost2 = cost2 * cost2 * Inputs.A; if (cost1 + cost2 < bestCost) { bestT = 2; bestR = j; bestCost = cost1 + cost2; } } if (bestR != -1) { res.Route.Add((bestT, bestR)); } res.Route.Add((1, order[i])); res.Cost += bestCost; } return res; } Outputs Yaki(Inputs inputs, Outputs outputs, int[] order) { var best = outputs; var random = new Random(1728); while (inputs.Sw.ElapsedMilliseconds < 970) { var stations = new List<(int y, int x)>(best.Stations); var idx = random.Next(0, stations.Count); var y = Math.Max(0, Math.Min(1000, stations[idx].y + random.Next(21) - 10)); var x = Math.Max(0, Math.Min(1000, stations[idx].x + random.Next(21) - 10)); stations[idx] = (y, x); var work = GetNextRoute(inputs, outputs, stations, order); if (work.Cost < best.Cost) { best = work; } } return best; } } public class TwoOpt { public readonly int NodeCount; public readonly int NodeCount2; public readonly double[] Costs; /// idx番目に経由する点がvalue番目の要素 public readonly int[] IdxByOrders; Random _random; System.Diagnostics.Stopwatch _sw; public TwoOpt(int n, double[] costs, int[] defaultOrder, Random random, System.Diagnostics.Stopwatch sw) { NodeCount = n; NodeCount2 = defaultOrder.Length; Costs = costs; IdxByOrders = new int[NodeCount2]; Array.Copy(defaultOrder, IdxByOrders, NodeCount2); _random = random; _sw = sw; } public void Exec() { //var cnt = 0; var costsSpan = new Span(Costs); while (_sw.ElapsedMilliseconds < 400) { for (int i = 0; i < 100; i++) { // 先頭、末尾は交換しない var from1order = _random.Next(NodeCount2 - 4); var from2order = _random.Next(from1order + 3, NodeCount2); var from1 = IdxByOrders[from1order]; var from2 = IdxByOrders[from2order]; var to1 = IdxByOrders[from1order + 1]; var to2 = IdxByOrders[from2order - 1]; // コスト計算 var oldRouteCost = costsSpan[from1 * NodeCount + to1] + costsSpan[from2 * NodeCount + to2]; var newRouteCost = costsSpan[from1 * NodeCount + to2] + costsSpan[from2 * NodeCount + to1]; // 改善されていれば更新 if (newRouteCost < oldRouteCost) Swap(from1order, from2order); } //cnt++; } } /// /// 2点の接続を交換する /// /// 点1のOrder順 /// 点2のOrder順 public void Swap(int from1order, int from2order) { Array.Reverse(IdxByOrders, from1order + 1, from2order - from1order - 1); } }