結果
問題 | No.5007 Steiner Space Travel |
ユーザー | Jiro_tech15 |
提出日時 | 2023-04-30 17:24:39 |
言語 | C++17 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 957 ms / 1,000 ms |
コード長 | 28,603 bytes |
コンパイル時間 | 4,372 ms |
コンパイル使用メモリ | 207,408 KB |
実行使用メモリ | 154,484 KB |
スコア | 8,895,460 |
最終ジャッジ日時 | 2023-04-30 17:25:20 |
合計ジャッジ時間 | 34,482 ms |
ジャッジサーバーID (参考情報) |
judge14 / judge13 |
純コード判定しない問題か言語 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 922 ms
153,532 KB |
testcase_01 | AC | 837 ms
152,016 KB |
testcase_02 | AC | 873 ms
152,320 KB |
testcase_03 | AC | 870 ms
152,248 KB |
testcase_04 | AC | 884 ms
153,164 KB |
testcase_05 | AC | 842 ms
151,852 KB |
testcase_06 | AC | 889 ms
152,904 KB |
testcase_07 | AC | 838 ms
151,648 KB |
testcase_08 | AC | 863 ms
151,948 KB |
testcase_09 | AC | 891 ms
153,084 KB |
testcase_10 | AC | 839 ms
151,628 KB |
testcase_11 | AC | 853 ms
151,908 KB |
testcase_12 | AC | 873 ms
153,000 KB |
testcase_13 | AC | 906 ms
152,720 KB |
testcase_14 | AC | 834 ms
151,624 KB |
testcase_15 | AC | 826 ms
150,892 KB |
testcase_16 | AC | 917 ms
154,484 KB |
testcase_17 | AC | 884 ms
152,896 KB |
testcase_18 | AC | 858 ms
152,544 KB |
testcase_19 | AC | 932 ms
153,004 KB |
testcase_20 | AC | 957 ms
152,628 KB |
testcase_21 | AC | 916 ms
152,296 KB |
testcase_22 | AC | 868 ms
152,848 KB |
testcase_23 | AC | 917 ms
153,532 KB |
testcase_24 | AC | 835 ms
150,836 KB |
testcase_25 | AC | 856 ms
151,860 KB |
testcase_26 | AC | 844 ms
152,300 KB |
testcase_27 | AC | 894 ms
153,172 KB |
testcase_28 | AC | 864 ms
152,580 KB |
testcase_29 | AC | 903 ms
153,080 KB |
コンパイルメッセージ
main.cpp: 静的メンバ関数 ‘static void Pos::StaticInit(int)’ 内: main.cpp:426:24: 警告: ‘dir’ may be used uninitialized [-Wmaybe-uninitialized] 426 | move_to[dir][p] = {adj_x, adj_y}; | ^ main.cpp:413:17: 備考: ‘dir’ はここで宣言されています 413 | Dir dir; | ^~~
ソースコード
namespace atcoder {} #ifdef LOCAL #define dbg(x) cerr << __LINE__ << " : " << #x << " = " << (x) << endl; #else #define NDEBUG #define dbg(x) true; #pragma GCC target("avx2") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #endif #ifdef GTEST #include <gtest/gtest.h> #endif #include <math.h> #include <algorithm> #include <array> #include <bitset> #include <cassert> #include <chrono> #include <cstdlib> #include <cstring> #include <functional> #include <iomanip> #include <iostream> #include <limits> #include <list> #include <map> #include <numeric> #include <queue> #include <set> #include <sstream> #include <stack> #include <string> #include <tuple> #include <unordered_map> #include <unordered_set> #include <vector> #ifdef PERF #include <gperftools/profiler.h> #endif using namespace std; using namespace atcoder; #define fast_io \ ios_base::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0); #define ll long long int #define rep(i, n) for (int i = 0; i < (int)(n); i++) #define reps(i, n) for (int i = 1; i <= (int)(n); i++) #define REP(i, n) for (int i = n - 1; i >= 0; i--) #define REPS(i, n) for (int i = n; i > 0; i--) #define MOD (long long int)(1e9 + 7) #define INF (int)(1e9) #define LINF (long long int)(1e18) #define chmax(a, b) a = (((a) < (b)) ? (b) : (a)) #define chmin(a, b) a = (((a) > (b)) ? (b) : (a)) #define all(v) v.begin(), v.end() typedef pair<int, int> Pii; typedef pair<ll, ll> Pll; constexpr double PI = acos(-1); #ifdef NDEBUG #define CHECK(v1, op, v2) #else #define CHECK(v1, op, v2) \ if (!((v1)op(v2))) { \ cerr << "ERROR:" << (v1) << " " << (v2) << endl; \ assert((v1)op(v2)); \ } #endif long double nCr(const int n, const int r) { long double ret = 1; rep(t, r) { ret *= (n - t); ret /= (r - t); } return ret; } template <typename T> string to_string(const vector<T>& vec) { string ret = ""; rep(i, vec.size()) { ret += vec[i].to_string(); if (i + 1 != vec.size()) { ret += ","; } } return ret; } template <typename T> ostream& operator<<(ostream& os, const vector<T>& vec) { os << to_string(vec); return os; } uint32_t xorshift() { static uint32_t x = 12345789; static uint32_t y = 362436069; static uint32_t z = 521288629; static uint32_t w = 88675123; uint32_t t; t = x ^ (x << 11); x = y; y = z; z = w; w ^= t ^ (t >> 8) ^ (w >> 19); return w; } int rand(const uint32_t l, const uint32_t r) { return xorshift() % (r - l) + l; } uint32_t rand_other_than(const uint32_t l, const uint32_t r, const uint32_t other) { const uint32_t num = rand(l, r - 1); return num + (num >= other); } template <typename T> const T& rand_vec(const vector<T>& vec) { assert(vec.size() > 0); return vec[rand(0, vec.size())]; } template <typename T> void shuffle(vector<T>& vec) { rep(l, (int)vec.size() - 1) { const int idx = rand(l, vec.size()); swap(vec[idx], vec[l]); } } /* start */ vector<double> PARAMS = {}; class Timer { chrono::system_clock::time_point _start, _end; ll _sum = 0, _count = 0; public: void start() { _start = chrono::system_clock::now(); } void stop() { _end = chrono::system_clock::now(); } void add() { const chrono::system_clock::time_point now = chrono::system_clock::now(); _sum += static_cast<double>( chrono::duration_cast<chrono::nanoseconds>(now - _start).count()); _count++; } ll sum() const { return _sum / 1000; } int count() const { return _count; } string average() const { if (_count == 0) { return "NaN"; } return to_string(_sum / 1000 / _count); } void reset() { _start = chrono::system_clock::now(); _sum = 0; _count = 0; } inline int ms() const { const chrono::system_clock::time_point now = chrono::system_clock::now(); return static_cast<double>( chrono::duration_cast<chrono::microseconds>(now - _start).count() / 1000); } inline int ns() const { const chrono::system_clock::time_point now = chrono::system_clock::now(); return static_cast<double>( chrono::duration_cast<chrono::microseconds>(now - _start).count()); } }; #ifdef LOCAL struct Timers : unordered_map<string, Timer> { friend ostream& operator<<(ostream& os, const Timers& timers) { for (const auto& pa : timers) { os << pa.first << " time: " << pa.second.sum() / 1000 << " count: " << pa.second.count() << endl; } return os; } }; #else struct Timers { struct Dummy { void start() const {} void add() const {} }; Dummy dummy; const Dummy& operator[](const std::string& str) { return dummy; } friend ostream& operator<<(ostream& os, const Timers& timers) { return os; } }; #endif Timers global_timers; // NBD = neighborhood template <class Solution, class NBDGenerator> struct SimulatedAnnealing { public: SimulatedAnnealing(double end_time, double start_temp, double end_temp) : end_time_(end_time * 1000), // ns inv_time_(1.0 / end_time_), cur_time_(0.0), start_temp_(start_temp), end_temp_(end_temp), diff_temp_(end_temp - start_temp), cur_temp_(start_temp) { assert(start_temp >= end_temp); timer_.start(); } Solution run(const Solution& initial_sol, NBDGenerator& nbd_generator) { Solution sol(initial_sol); int acc = 0; int g = 0; for (;; ++g) { if ((g & 0xf) == 0) { #ifdef SA_MAX_G if (g >= SA_MAX_G) { break; } #endif UpdateTime(); UpdateTemp(); if (cur_time_ > end_time_) { break; } } const auto nbd = nbd_generator.Generate(&sol, g); constexpr int mod = 1'000'000'000; const int r = rand(0, mod); if (static_cast<double>(r) / mod < exp(-nbd->Diff() / cur_temp_)) { acc++; nbd->Update(&sol); } else { nbd->Restore(&sol); } } cerr << "g,acc: " << g << " " << acc << endl; return sol; } private: double end_time_, inv_time_, cur_time_; double start_temp_, end_temp_, diff_temp_, cur_temp_; Timer timer_; void UpdateTime() { cur_time_ = timer_.ns(); } void UpdateTemp() { cur_temp_ = max(0.0, start_temp_ - cur_time_ * diff_temp_ * inv_time_); } }; /* start */ template <typename Point, typename Dist_T> class Graph_T { public: struct Edge { Point to; Dist_T dist; Edge(const Point _to, const Dist_T _dist) : to(_to), dist(_dist) {} }; Graph_T() {} explicit Graph_T(const int n) : n_(n), adjs_(n), dists_(n, vector<Dist_T>(n)) {} int N() const { return n_; } Dist_T Dist(const Point& from, const Point& to) const { return dists_[from][to]; } void AddEdge(const Point a, const Point b, const Dist_T dist) { adjs_[a].emplace_back(b, dist); dists_[a][b] = dist; } const vector<Edge>& Adj(const Point a) const { return adjs_[a]; } void RemoveEdge(const Point a, const Point b) { { const auto itr = std::find_if( all(adjs_[a]), [&](const Edge& edge) { return edge.to == b; }); assert(itr != adjs_[a].end()); adjs_[a].erase(itr); dists_[a][b] = INF; } } private: int n_; vector<vector<Edge>> adjs_; vector<vector<Dist_T>> dists_; }; enum Dir { // y-1, y+1, x-1, x+1 kU, kD, kL, kR }; struct Pos { int idx_; Pos() {} explicit Pos(const int _idx) : idx_(_idx) {} Pos(int _x, int _y) : idx_(_y * N + _x) { assert(N > 0); } int X() const { return pos_2_x[*this]; } int Y() const { return pos_2_y[*this]; } int Idx() const { return idx_; } operator int() const { return Idx(); } operator size_t() const { return Idx(); } const vector<Pos>& Adj() const { return adj_poses[*this]; } const vector<Dir>& AdjDirs() const { return adj_dirs[*this]; } int Manhattan(const Pos& other) const { return abs(X() - other.X()) + abs(Y() - other.Y()); } int Euclid2(const Pos& other) const { const int dx = X() - other.X(); const int dy = Y() - other.Y(); return dx * dx + dy * dy; } bool Move(const Dir dir) { if (move_to[dir][*this].IsDummy()) { return false; } else { *this = move_to[dir][*this]; return true; } } bool operator<(const Pos& other) const { return this->Idx() < other.Idx(); } bool operator==(const Pos& other) const { return this->Idx() == other.Idx(); } bool operator!=(const Pos& other) const { return this->Idx() != other.Idx(); } friend ostream& operator<<(ostream& os, const Pos& pos) { os << pos.X() << " " << pos.Y(); return os; } bool IsDummy() const { return this->idx_ < 0; } static Pos Dummy() { Pos p; p.idx_ = -1; return p; } static Pos TryCreate(const int x, const int y, const int z) { if (y < 0 || y >= N || x < 0 || x >= N) { return Pos::Dummy(); } else { return Pos(x, y); } } static void StaticInit(const int n) { N = n; N2 = N * N; N4 = N2 * N2; adj_poses = vector<vector<Pos>>(N2); adj_dirs = vector<vector<Dir>>(N2); move_to = vector<vector<Pos>>(4, vector<Pos>(N2, Pos::Dummy())); pos_2_y.resize(N2); pos_2_x.resize(N2); rep(y, N) { rep(x, N) { const Pos p{x, y}; pos_2_y[p] = y; pos_2_x[p] = x; for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { if (abs(dy) + abs(dx) != 1) continue; const int adj_y = y + dy; const int adj_x = x + dx; if (adj_y < 0 || adj_y >= N) continue; if (adj_x < 0 || adj_x >= N) continue; adj_poses[p].emplace_back(adj_x, adj_y); Dir dir; if (dy == -1) { dir = kU; } else if (dy == 1) { dir = kD; } else if (dx == -1) { dir = kL; } else if (dx == 1) { dir = kR; } else { assert(false); } move_to[dir][p] = {adj_x, adj_y}; adj_dirs[p].emplace_back(dir); } } } } } static int N, N2, N4; static vector<vector<Pos>> adj_poses; static vector<vector<Dir>> adj_dirs; static vector<vector<Pos>> move_to; static vector<int> pos_2_x, pos_2_y; }; vector<vector<Pos>> Pos::adj_poses; vector<vector<Dir>> Pos::adj_dirs; vector<vector<Pos>> Pos::move_to; vector<int> Pos::pos_2_x, Pos::pos_2_y; int Pos::N, Pos::N2, Pos::N4; /* start */ constexpr int kPlanetType = 1; constexpr int kStationType = 2; struct Solution { using Graph = Graph_T<int, int>; Solution() { orders.resize(N); std::iota(all(orders), 0); stations.resize(M); rep(i, M) { stations[i] = Pos{rand(0, Pos::N + 1), rand(0, Pos::N + 1)}; } } static void StaticInit(const int _N, const int _M, const vector<Pos>& _planets, const Graph& _graph) { N = _N; M = _M; planets = _planets; graph = _graph; } // [l, r)のreverse int DiffSwap(const int l, const int r) const { // l-1, l, r-1, r // l-1, r-1, l, r const int i0 = (l - 1 + N) % N; const int i1 = l % N; const int i2 = (r - 1 + N) % N; const int i3 = r % N; const int prev_dist = graph.Dist(orders[i0], orders[i1]) + graph.Dist(orders[i2], orders[i3]); const int next_dist = graph.Dist(orders[i0], orders[i2]) + graph.Dist(orders[i1], orders[i3]); return next_dist - prev_dist; } void Swap(int l, int r) { if (l > r) { swap(l, r); } reverse(orders.begin() + l, orders.begin() + r); } vector<pair<int, int>> CalcAns() const { int start_i = 0; for (start_i = 0; start_i < N; start_i++) { if (this->orders[start_i] == 0) { break; } } vector<pair<int, int>> ans; ans.emplace_back(kPlanetType, this->orders[start_i]); rep(add_i, N) { const int i = (start_i + add_i) % N; const int next_i = (i + 1) % N; const Pos& from_planet = planets[this->orders[i]]; const Pos& to_planet = planets[this->orders[next_i]]; // stationを検討 int min_dist = Solution::graph.Dist(this->orders[i], this->orders[next_i]); vector<int> best_stations; // i,jの順に経由 rep(i, M) { const Pos& station_i = this->stations[i]; rep(j, M) { if (i == j) continue; const Pos& station_j = this->stations[j]; const int dist = from_planet.Euclid2(station_i) * alpha + station_i.Euclid2(station_j) + station_j.Euclid2(to_planet) * alpha; if (dist < min_dist) { min_dist = dist; best_stations = {i, j}; } } } // iのみ経由 rep(i, M) { const Pos& station = this->stations[i]; const int dist = from_planet.Euclid2(station) * alpha + station.Euclid2(to_planet) * alpha; if (dist < min_dist) { min_dist = dist; best_stations = {i}; } } if (best_stations.size() == 0) { // 惑星to惑星 int from = this->orders[i]; int to = this->orders[next_i]; vector<int> rev_planets; while (from != to) { rev_planets.emplace_back(to); rep(i, N) { if (i == to) continue; if (graph.Dist(from, i) + graph.Dist(i, to) == graph.Dist(from, to)) { to = i; break; } } } REP(i, rev_planets.size()) { ans.emplace_back(kPlanetType, rev_planets[i]); } } else { for (const auto station : best_stations) { ans.emplace_back(kStationType, station); } ans.emplace_back(kPlanetType, this->orders[next_i]); } } return ans; } friend ostream& operator<<(ostream& os, const Solution& sol) { const auto ans = sol.CalcAns(); rep(i, M) { os << sol.stations[i] << endl; } os << ans.size() << endl; rep(i, ans.size()) { const auto& [t, r] = ans[i]; os << t << " " << r + 1; if (i + 1 != (int)ans.size()) { os << endl; } } int dist_sum = 0; rep(i, (int)ans.size() - 1) { const auto& [t0, r0] = ans[i]; const auto& [t1, r1] = ans[(i + 1) % ans.size()]; const Pos& from = (t0 == kPlanetType ? planets[r0] : sol.stations[r0]); const Pos& to = (t1 == kPlanetType ? planets[r1] : sol.stations[r1]); dist_sum += from.Euclid2(to) * (t0 == kPlanetType ? alpha : 1) * (t1 == kPlanetType ? alpha : 1); } cerr << "score: " << std::round(1e9 / (1000 + sqrt(dist_sum))) << endl; return os; } static vector<Pos> planets; static int N, M; static Graph graph; static constexpr int alpha = 5; static constexpr int alpha2 = 25; vector<int> orders; vector<Pos> stations; }; int Solution::N; int Solution::M; vector<Pos> Solution::planets; Graph_T<int, int> Solution::graph; /* start */ struct TSPSolution { using Graph = Graph_T<int, int>; TSPSolution() { orders.resize(N); std::iota(all(orders), 0); } TSPSolution(const vector<int>& _orders) : orders(_orders) {} static void StaticUpdateGraph(const Graph& _graph) { graph = _graph; N = _graph.N(); } // [l, r)のreverse int DiffSwap(const int l, const int r) const { // l-1, l, r-1, r // l-1, r-1, l, r const int i0 = (l - 1 + N) % N; const int i1 = l % N; const int i2 = (r - 1 + N) % N; const int i3 = r % N; const int prev_dist = graph.Dist(orders[i0], orders[i1]) + graph.Dist(orders[i2], orders[i3]); const int next_dist = graph.Dist(orders[i0], orders[i2]) + graph.Dist(orders[i1], orders[i3]); return next_dist - prev_dist; } void Swap(int l, int r) { if (l > r) { swap(l, r); } reverse(orders.begin() + l, orders.begin() + r); } // toの前にtargetを入れる int DiffInsert(const int to, const int target) const { // to-1, to, target-1, target, target, target+1 // to-1, target, target, to, target-1, target+1 const int i0 = (to - 1 + N) % N; const int i1 = to; const int i2 = (target - 1 + N) % N; const int i3 = target; const int i4 = target; const int i5 = (target + 1) % N; const int prev_dist = graph.Dist(orders[i0], orders[i1]) + graph.Dist(orders[i2], orders[i3]) + graph.Dist(orders[i4], orders[i5]); const int next_dist = graph.Dist(orders[i0], orders[i3]) + graph.Dist(orders[i4], orders[i1]) + graph.Dist(orders[i2], orders[i5]); return next_dist - prev_dist; } void Insert(const int to, const int target) { orders.insert(orders.begin() + to, orders[target]); orders.erase(orders.begin() + target + (to <= target ? 1 : 0)); } static Graph graph; static int N; vector<int> orders; }; int TSPSolution::N; Graph_T<int, int> TSPSolution::graph; /* start */ using Graph = Graph_T<int, int>; struct NBD { int Diff() const { return diff_; } virtual void Update(TSPSolution*) const = 0; void Restore(TSPSolution*) const {} int diff_; }; struct NBD_swap : public NBD { NBD_swap() {} NBD_swap(TSPSolution* const sol, const int l, const int r) { l_ = l; r_ = r; diff_ = sol->DiffSwap(l, r); } void Update(TSPSolution* sol) const override { sol->Swap(l_, r_); } int l_; int r_; }; struct NBD_insert : public NBD { NBD_insert() {} NBD_insert(TSPSolution* const sol, const int to, const int target) { to_ = to; target_ = target; diff_ = sol->DiffInsert(to, target); } void Update(TSPSolution* sol) const override { sol->Insert(to_, target_); } int to_; int target_; }; struct NBDGenerator { NBDGenerator() {} NBD* Generate(TSPSolution* const sol, const int g) { if ((g & 0x4) == 0) { // 1.5-opt const int to = rand(0, TSPSolution::N); const int target = rand(0, TSPSolution::N); nbd_insert0 = NBD_insert(sol, to, target); return &nbd_insert0; } else { // 2-opt const int l = rand(0, TSPSolution::N); const int r = rand_other_than(0, TSPSolution::N, l); nbd_swap0 = NBD_swap(sol, l, r); return &nbd_swap0; } } NBD_swap nbd_swap0; // 2-opt NBD_insert nbd_insert0; // 1.5-opt }; struct State { static State Dummy() { State state; state.sum_dist = INF; return state; } static void StaticInit(const int _N, const int _M, const vector<Pos>& _planets) { N = _N; M = _M; planets = _planets; } bool IsDummy() const { return sum_dist == INF; } void InitWarshall() { warshall = vector<vector<int>>(N + M, vector<int>(N + M, 0)); } void UpdateWarshall() { if (stations.size() == 0) return; const int total_N = N + stations.size(); const int k = total_N - 1; const Pos last_station = stations.back(); // kにまっすぐ向かった時の距離 vector<int> dist_to_k_straight(total_N); rep(i, N) { dist_to_k_straight[i] = planets[i].Euclid2(last_station) * alpha; } for (int i = N; i < total_N; ++i) { dist_to_k_straight[i] = stations[i - N].Euclid2(last_station); } // iからkへの最短距離をまずは求める rep(i, total_N) { int min_dist = INF; rep(j, total_N - 1) { const int dist = warshall[i][j] + dist_to_k_straight[j]; chmin(min_dist, dist); } assert(min_dist < INF); warshall[i][k] = warshall[k][i] = min_dist; } // warshallを更新 warshall[k][k] = 0; rep(i, total_N) { rep(j, i) { const int next_dist = warshall[i][k] + warshall[k][j]; if (next_dist < warshall[i][j]) { warshall[i][j] = warshall[j][i] = next_dist; } } } } int CalcSumDist() const { int ret = 0; rep(i, orders.size()) { const int next_i = (i + 1) % orders.size(); ret += warshall[orders[i]][orders[next_i]]; } return ret; } void TwoOpt(const double time_limit) { SimulatedAnnealing<TSPSolution, NBDGenerator> sa(time_limit, 1.0, 0.03); NBDGenerator nbd_generator; Graph graph(N); rep(i, N) { rep(j, N) { graph.AddEdge(i, j, warshall[i][j]); } } TSPSolution::StaticUpdateGraph(graph); assert(orders.size() == N); TSPSolution initial_sol(orders); const auto tsp_sol = sa.run(initial_sol, nbd_generator); orders = tsp_sol.orders; sum_dist = CalcSumDist(); } vector<vector<int>> warshall; vector<int> orders; vector<Pos> stations; int sum_dist; int prev_j; int prev_k; static constexpr int alpha = 5; static constexpr int alpha2 = 25; static vector<Pos> planets; static int N, M; }; int State::N; int State::M; vector<Pos> State::planets; class Solver { public: Solver(istream& is) { is >> N >> M; Pos::StaticInit(1001); planets.reserve(N); rep(i, N) { int a, b; is >> a >> b; planets.emplace_back(a, b); } warshall_without_stations = CalcWarshallWithoutStations(); const auto graph = InitGraph(warshall_without_stations); TSPSolution::StaticUpdateGraph(graph); Solution::StaticInit(N, M, planets, graph); State::StaticInit(N, M, planets); } vector<vector<int>> CalcWarshallWithoutStations() const { // stationの分も確保しておく vector<vector<int>> dists(N + M, vector<int>(N + M, INF)); rep(i, N + M) { dists[i][i] = 0; } rep(i, N) { rep(j, N) { dists[i][j] = alpha2 * planets[i].Euclid2(planets[j]); } } rep(k, N) { rep(i, N) { rep(j, N) { chmin(dists[i][j], dists[i][k] + dists[k][j]); } } } return dists; } Graph InitGraph(const vector<vector<int>>& dists) const { // dists.size() = N+M // graph.size() = N Graph graph(N); rep(i, N) { rep(j, N) { const int dist = dists[i][j]; graph.AddEdge(i, j, dist); } } return graph; } using BS = bitset<100>; vector<pair<Pos, int>> GridSearchNextStation(const State& state, const int delta) const { vector<pair<Pos, int>> best_next_stations(N + 1, {Pos::Dummy(), INF}); assert(state.orders.size() == N); for (int y = 0; y <= 1000; y += delta) { for (int x = 0; x <= 1000; x += delta) { const Pos station(x, y); // このstationの経由を可能としたときの、各辺のdistを求める int dist_sum = 0; int used_station_count = 0; rep(order_i, N) { const int i = state.orders[order_i]; const int j = state.orders[(order_i + 1) % N]; const auto pos_i = planets[i]; const auto pos_j = planets[j]; const int used_dist = (pos_i.Euclid2(station) + pos_j.Euclid2(station)) * alpha; const int dist = min(state.warshall[i][j], used_dist); dist_sum += dist; used_station_count += (dist < warshall_without_stations[i][j]); } if (best_next_stations[used_station_count].second > dist_sum) { best_next_stations[used_station_count] = {station, dist_sum}; } } } return best_next_stations; } Solution BeamSearch(const vector<int>& initial_orders) const { assert(initial_orders.size() == N); assert(warshall_without_stations.size() == N + M); vector dp(M + 1, vector(N + 1, State::Dummy())); dp[0][0].warshall = warshall_without_stations; dp[0][0].orders = initial_orders; dp[0][0].sum_dist = dp[0][0].CalcSumDist(); rep(j, M + 1) { rep(k, N + 1) { auto& state = dp[j][k]; if (state.IsDummy()) continue; // TSPを追加で1ms行い、既存の駅を有効活用できている状態にする if (j != 0) { state.warshall = dp[state.prev_j][state.prev_k].warshall; } state.UpdateWarshall(); state.TwoOpt(0.8); if (j == M) continue; // 訪問順は固定した状態で、次の駅を置く場所をグリッドサーチして、 //「何らかの駅を経由している辺の本数」ごとに「total_distが最小」となるような // 駅の置き場所を求める const auto best_next_stations = GridSearchNextStation(state, 33); // それらを用いて遷移 rep(next_k, N + 1) { const auto& [station, dist] = best_next_stations[next_k]; if (dist == INF) continue; auto& next_state = dp[j + 1][next_k]; if (next_state.sum_dist <= dist) continue; next_state.orders = state.orders; next_state.stations = state.stations; next_state.stations.emplace_back(station); next_state.sum_dist = dist; next_state.prev_j = j; next_state.prev_k = k; } } } using IDX = tuple<int, int>; IDX best_idx; int min_dist = INF; const int j = M; rep(k, N + 1) { auto& state = dp[j][k]; if (state.IsDummy()) continue; if (state.sum_dist < min_dist) { min_dist = state.sum_dist; best_idx = {j, k}; } } #if 0 IDX idx = best_idx; while (true) { const auto [j, k] = idx; const auto& state = dp[j][k]; Solution sol; sol.orders = state.orders; sol.stations = state.stations; sol.stations.resize(M, Pos{0, 0}); cout << endl << endl << sol << endl; if (j == 0) { break; } idx = {state.prev_j, state.prev_k}; } #endif cerr << "min_dist: " << min_dist << endl; const auto& [best_j, best_k] = best_idx; Solution best_sol; best_sol.orders = dp[best_j][best_k].orders; best_sol.stations = dp[best_j][best_k].stations; return best_sol; } Solution OptimizeStations(const Solution& base_sol) const { const auto base_ans = base_sol.CalcAns(); // 各stationから伸びている辺のdistの総和を最小化する位置に各stationを移動する // 重心に移動すればよい // station->stationについては、重みを1/5にするのが自然だが、 // もう一つのstationもずれることが予想されるので、重みは1/10にする vector<float> weight_sums(M); vector<float> Xs(M), Ys(M); rep(i, (int)base_ans.size() - 1) { auto [t0, r0] = base_ans[i]; auto [t1, r1] = base_ans[i + 1]; Pos p0 = (t0 == kPlanetType ? planets[r0] : base_sol.stations[r0]); Pos p1 = (t1 == kPlanetType ? planets[r1] : base_sol.stations[r1]); rep(_t, 2) { swap(t0, t1); swap(r0, r1); swap(p0, p1); // 0について考える if (t0 != kStationType) continue; // station->planetなら1.0 // station->stationなら0.1 const float weight = (t1 == kPlanetType ? 1.0 : 0.1); weight_sums[r0] += weight; Xs[r0] += p1.X() * weight; Ys[r0] += p1.Y() * weight; } } auto sol = base_sol; rep(i, M) { const auto weight = weight_sums[i]; assert(weight > 0.0); float float_x = Xs[i] / weight; float float_y = Ys[i] / weight; const int x = std::clamp((int)std::round(float_x), 0, 1000); const int y = std::clamp((int)std::round(float_y), 0, 1000); sol.stations[i] = Pos{x, y}; } return sol; } Solution Solve(const int time_limit) const { const TSPSolution initial_sol; SimulatedAnnealing<TSPSolution, NBDGenerator> sa(100, 1.0, 0.03); NBDGenerator nbd_generator; const auto tsp_sol = sa.run(initial_sol, nbd_generator); const auto beam_sol = BeamSearch(tsp_sol.orders); const auto optimized_sol = OptimizeStations(beam_sol); return optimized_sol; } private: int N, M; vector<vector<int>> warshall_without_stations; vector<Pos> planets; static constexpr int alpha = 5; static constexpr int alpha2 = 25; }; int main(int argc, char* argv[]) { fast_io; if (argc >= 2) { int idx = 0; for (int i = 1; i < argc; ++i) { PARAMS[idx++] = std::stod(argv[i]); } } Timer timer; timer.start(); Solver solver(cin); auto sol = solver.Solve(900 - timer.ms()); cerr << "ms: " << timer.ms() << endl; cout << sol << endl; return 0; }