#include #include #include #include #include #include #include #include #include #include using namespace std; unsigned int xor128() { static unsigned int x=123456789, y=362436069, z=521288629, w=88675123; unsigned int t; t=(x^(x<<11)); x=y; y=z; z=w; return (w=(w^(w>>19))^(t^(t>>8))); } inline bool rand_bool(double prob) { constexpr double x = 1LL<<32; // uint_max+1 return xor128() < prob * x; } class Timer { chrono::system_clock::time_point start_time = chrono::system_clock::now(); public: Timer() {} long long get_elapsed_time() { auto diff = chrono::system_clock::now() - start_time; return chrono::duration_cast(diff).count(); } } timer; using P = pair; constexpr int N = 100, M = 8; int a[N], b[N]; mt19937 mt; constexpr int alpha = 5; int L1(P p, P q) { return abs(p.first - q.first) + abs(p.second - q.second); } int L22(int x1, int y1, int x2, int y2) { const int dx = x1 - x2, dy = y1 - y2; return dx * dx + dy * dy; } int L22(P p, P q) { return L22(p.first, p.second, q.first, q.second); } int cost(int i, int j) { return L22(a[i], b[i], a[j], b[j]) * alpha * alpha; } int cost(int i, int j, P middle) { return (L22(a[i], b[i], middle.first, middle.second) + L22(a[j], b[j], middle.first, middle.second)) * alpha; } int cost(int t1, P p1, int t2, P p2) { const int coeff = (t1 == 1 && t2 == 1 ? alpha * alpha : t1 == 1 || t2 == 1 ? alpha : 1); return coeff * L22(p1, p2); } array, N> mincost; array, N> next_idx; void calc_mincost() { for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) { mincost[i][j] = cost(i, j); next_idx[i][j] = j; } for (int k = 0; k < N; k++) for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) if (mincost[i][k] + mincost[k][j] < mincost[i][j]) { mincost[i][j] = mincost[i][k] + mincost[k][j]; assert(i != k); assert(j != k); next_idx[i][j] = next_idx[i][k]; } } void read_input() { { int n, m; cin >> n >> m; assert(n == N); assert(m == M); } for (int i = 0; i < N; i++) cin >> a[i] >> b[i]; calc_mincost(); } vector solve_tsp(int tl) { vector ans(N); iota(ans.begin(), ans.end(), 0); int total_cost = 0; for (int i = 0; i < N; i++) { if (i + 1 < N) total_cost += mincost[i][i + 1]; else total_cost += mincost[0][N - 1]; } Timer tsp_timer; uniform_int_distribution randindex(0, N - 1); int iter = 0, last_upd = 0, stop_threshold = N * N; while (tsp_timer.get_elapsed_time() < tl && last_upd + stop_threshold > iter) { int i = randindex(mt), j = randindex(mt); assert(i < N); assert(j < N); if (i == j) continue; if (i > j) swap(i, j); if (i + 1 == j) continue; iter++; const int k = (j + 1 == N ? 0 : j + 1); // const int diff = (cost(ans[i], ans[j]) + cost(ans[i + 1], ans[k]) // - cost(ans[i], ans[i + 1]) - cost(ans[j], ans[k])); const int diff = (mincost[ans[i]][ans[j]] + mincost[ans[i + 1]][ans[k]] - mincost[ans[i]][ans[i + 1]] - mincost[ans[j]][ans[k]]); if (diff <= 0) { reverse(ans.begin() + i + 1, ans.begin() + j + 1); total_cost += diff; last_upd = iter; // cerr << iter << ' ' << total_cost << endl; } } cerr << iter << ' ' << total_cost << endl; return ans; } class DSU { vector par, rank; public: explicit DSU(int n) : par(n), rank(n, 0) { iota(par.begin(), par.end(), 0); } int find(int i) { return (par[i] == i) ? i : (par[i] = find(par[i])); } void merge(int i, int j) { int x = find(i), y = find(j); if (x != y) { if (rank[x] < rank[y]) par[x] = y; else { par[y] = x; if (rank[x] == rank[y]) rank[x]++; } } } bool same(int i, int j) { return find(i) == find(j); } }; struct Target { int type, idx; Target(int type, int idx) : type(type), idx(idx) { assert(type == 1 || type == 2); } bool operator!=(const Target &rhs) { return type != rhs.type || idx != rhs.idx; } }; struct Config { vector path; array stations; }; pair, long long> greedy(const Config &conf) { vector ans; array visited; fill(visited.begin(), visited.end(), false); ans.emplace_back(1, conf.path[0]); visited[conf.path[0]] = true; long long total_cost = 0; for (int i = 0; i + 1 < conf.path.size(); i++) { const int from = ans.back().idx, to = conf.path[i + 1]; if (visited[to] && i + 2 < conf.path.size()) continue; int cur_cost = mincost[from][to], s = -1; for (int j = 0; j < M; j++) { const int tmp = cost(from, to, conf.stations[j]); if (tmp < cur_cost) { cur_cost = tmp; s = j; } } if (s >= 0) { const auto [t1, i1] = ans.back(); assert(t1 == 1); total_cost += cost(1, make_pair(a[i1], b[i1]), 2, conf.stations[s]); ans.emplace_back(2, s); total_cost += cost(2, conf.stations[s], 1, make_pair(a[to], b[to])); ans.emplace_back(1, to); visited[to] = true; } else { for (int cur = from; cur != to; cur = next_idx[cur][to]) { const auto [t1, i1] = ans.back(); assert(t1 == 1); assert(i1 == cur); const int nxt = next_idx[cur][to]; total_cost += cost(1, make_pair(a[cur], b[cur]), 1, make_pair(a[nxt], b[nxt])); ans.emplace_back(1, nxt); visited[nxt] = true; } } } return {ans, total_cost}; } int timelimit = 1000, margin = 20; struct Solution { array stations; vector moves; }; Solution improve(const Solution &sol, int tl) { Timer timer; array, M + N> dist; array, M + N> next_idx; for (int i = 0; i < N + M; i++) for (int j = 0; j < N + M; j++) { if (i < N) { if (j < N) { dist[i][j] = cost(1, P(a[i], b[i]), 1, P(a[j], b[j])); } else { const P p1(a[i], b[i]), p2 = sol.stations[j - N]; dist[i][j] = cost(1, p1, 2, p2); } } else { if (j < N) { const P p1 = sol.stations[i - N], p2(a[j], b[j]); dist[i][j] = cost(2, p1, 1, p2); } else { const P p1 = sol.stations[i - N], p2 = sol.stations[j - N]; dist[i][j] = cost(2, p1, 2, p2); } } next_idx[i][j] = j; } for (int k = 0; k < N + M; k++) { for (int i = 0; i < N + M; i++) { for (int j = 0; j < N + M; j++) { const long long tmp = dist[i][k] + dist[k][j]; if (tmp < dist[i][j]) { dist[i][j] = tmp; next_idx[i][j] = next_idx[i][k]; } } } } vector path; vector visited(N, false); for (auto [t, i] : sol.moves) { if (t == 1 && !visited[i]) { path.push_back(i); visited[i] = true; } } path.push_back(0); const int n = int(path.size()); uniform_int_distribution randindex(0, n - 2); while (timer.get_elapsed_time() < tl) { int i = randindex(mt), j = randindex(mt); assert(i < n); assert(j < n); if (i == j) continue; if (i > j) swap(i, j); if (i + 1 == j) continue; // iter++; const int k = j + 1; const int diff = (dist[path[i]][path[j]] + dist[path[i + 1]][path[k]] - dist[path[i]][path[i + 1]] - dist[path[j]][path[k]]); if (diff <= 0) { reverse(path.begin() + i + 1, path.begin() + j + 1); // total_cost += diff; // last_upd = iter; // cerr << iter << ' ' << total_cost << endl; } } Solution newsol; newsol.stations = sol.stations; newsol.moves.emplace_back(1, 0); for (size_t i = 0; i + 1 < path.size(); i++) { const int from = path[i], to = path[i + 1]; for (int cur = from; cur != to; cur = next_idx[cur][to]) { const int nxt = next_idx[cur][to]; if (nxt < N) newsol.moves.emplace_back(1, nxt); else newsol.moves.emplace_back(2, nxt - N); } } return newsol; } int main() { read_input(); vector path = solve_tsp(200); { auto start = find(path.begin(), path.end(), 0); assert(start != path.end()); rotate(path.begin(), start, path.end()); assert(path.front() == 0); path.push_back(0); } array stations{ P{200, 333}, {200, 667}, {400, 333}, {400, 667}, {600, 333}, {600, 667}, {800, 333}, {800, 667}, }; auto [cur_ans, cur_cost] = greedy({path, stations}); Solution best_ans = {stations, cur_ans}; long long best_cost = cur_cost; int iter = 0; uniform_int_distribution rand_pos(10, 990); uniform_int_distribution rand_diff(-20, 20); while (timer.get_elapsed_time() + margin < 900) { iter++; const int i = iter % M; const P origp = stations[i], newp = [&] { if (timer.get_elapsed_time() < timelimit / 2) { return P(rand_pos(mt), rand_pos(mt)); } else { const int dx = rand_diff(mt), dy = rand_diff(mt); return P(clamp(origp.first + dx, 10, 990), clamp(origp.second + dy, 10, 990)); } }(); stations[i] = newp; const auto [tmp_ans, tmp_cost] = greedy({path, stations}); if (tmp_cost < best_cost) { cerr << iter << ' ' << tmp_cost << ' ' << round(1e9 / (1000 + sqrt(tmp_cost))) << endl; best_cost = tmp_cost; best_ans = {stations, tmp_ans}; // for (auto [x, y] : best_ans.stations) cout << x << ' ' << y << '\n'; } const long long diff = tmp_cost - cur_cost; bool accept = diff <= 0; if (!accept) { const double start_temp = 1e4, end_temp = 1, progress = double(timer.get_elapsed_time()) / timelimit, temp = start_temp + (end_temp - start_temp) * progress, prob = exp(-diff / temp); accept = rand_bool(prob); // cerr << diff << ' ' << prob << endl; } if (accept) { cur_cost = tmp_cost; cur_ans = tmp_ans; } else { // rollback stations[i] = origp; } } cerr << iter << ' ' << round(1e9 / (1000 + sqrt(best_cost))) << endl; best_ans = improve(best_ans, 100); // output for (auto [x, y] : best_ans.stations) cout << x << ' ' << y << '\n'; cout << best_ans.moves.size() << '\n'; for (auto [t, i] : best_ans.moves) cout << t << ' ' << i + 1 << '\n'; }