//------------------------------------------------------------------------------ /// @file /// @author ハル研究所プログラミングコンテスト実行委員会 /// /// @copyright (C)HAL Laboratory, Inc. /// @attention このファイルの利用は、同梱のREADMEにある /// 利用条件に従ってください。 //------------------------------------------------------------------------------ // 本作品について (nari) // 本作品は複数のヘッダファイルに分割して開発され,それらを1ファイルにまとめて // 提出されています.1ファイルにまとめるスクリプトをあまり賢く書けなかったため, // 複数回同じ記述が存在するなど若干読みづらい箇所がありますが,ご了承ください. // (using によるタイプエイリアスや,標準ライブラリの include など.) #include "Answer.hpp" #include <algorithm> #include <cassert> #include <cmath> #include <vector> // #include "local/config.hpp" // ============================== // local/config.hpp // ============================== #ifndef __CONFIG_HPP #define __CONFIG_HPP #include <vector> namespace hpc { // ============================================== // パラメータ一覧 // ============================================= // for specific stage executor #define SPECIFIC -1 // for corner points #define CORNER_DELTA 0.001 #define CORNER_LESS false // for augment points #define AUG_STEP 0.1 #define AUG_THRESHOLD 0.5 // for graph cons #define GRAPH_PRUNE 25.0f // for edge #define EDGE_MAX_LEN 80 // for apsp int APSP_NEIGHBOR = 30; #define APSP_MAX_LEN 300 // for atom optimizer #define ATOM_MAX_LEN 300 bool ATOM_DEVIATE = true; bool ATOM_STRICT = false; bool ATOM_SKIP = true; std::vector<bool> ATOM_CROSS({1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0}); float ATOM_CROSS_START = 0.0257152; float ATOM_CROSS_STEP = 0.07683342; float ATOM_CROSS_STOP = ATOM_CROSS_START + ATOM_CROSS_STEP * 9.5; int ATOM_OPT_NUM = ATOM_CROSS.size(); std::vector<float> ATOM_LENS({0.9584536, 1.65483455, 2.2260893}); std::vector<int> ATOM_NS({16, 4, 15}); // ============================================== // パラメータ一覧 ここまで // ============================================= } // namespace hpc #endif // ============================== // end of local/config.hpp // ============================== // #include "local/point.hpp" // ============================== // local/point.hpp // ============================== #ifndef __POINT_HPP #define __POINT_HPP #include <algorithm> #include <cmath> #include <vector> namespace hpc { // 点に関する構造の再定義と複数のメソッド定義 using Point = hpc::Vector2; Point operator+(const Point &a, const Point &b) { return Point(a.x + b.x, a.y + b.y); } Point operator-(const Point &a, const Point &b) { return Point(a.x - b.x, a.y - b.y); } Point operator*(const Point &a, double b) { return Point(a.x * b, a.y * b); } Point operator*(double b, const Point &a) { return Point(a.x * b, a.y * b); } Point operator/(const Point &a, double b) { return Point(a.x / b, a.y / b); } float abs(const Point &a) { return std::sqrt(a.x * a.x + a.y * a.y); } float norm(const Point &a) { return a.x * a.x + a.y * a.y; } bool in_stage(const Point &a) { return 0.0 <= a.x && a.x < 50.0 && 0.0 <= a.y && a.y < 50.0; } Point clamp_into_stage(const Point &a) { return Point(std::max(0.01f, std::min(49.9f, a.x)), std::max(0.01f, std::min(49.9f, a.y))); } float get_nearest_distance(const std::vector<Point> &pos, const Point &p) { float ret = 1e18; for (const Point &q : pos) { ret = std::min(ret, norm(p - q)); } return std::sqrt(ret); } Point get_nearest_point(const std::vector<Point> &pos, const Point &p) { float best_norm = 1e18; Point best = pos[0]; for (const Point &q : pos) { float v = norm(p - q); if (v < best_norm) { best_norm = v; best = q; } } return best; } } // namespace hpc #endif // ============================== // end of local/point.hpp // ============================== // #include "local/calc_length.hpp" // ============================== // local/calc_length.hpp // ============================== #ifndef __CALC_LENGTH_HPP #define __CALC_LENGTH_HPP // #include "config.hpp" // #include "point.hpp" #include <algorithm> #include <cmath> #include <utility> #include <vector> namespace hpc { using std::pair; using std::vector; using dist_type = pair<int, float>; namespace calc_field { Terrain terrain[50][50]; float tcoeff[50][50]; // 地形情報をコピーする void initialize_length_field(const Stage &stage) { for (int y = 0; y < 50; y++) { for (int x = 0; x < 50; x++) { terrain[y][x] = stage.terrain(Point(x, y)); tcoeff[y][x] = Parameter::JumpTerrianCoefficient[(int)terrain[y][x]]; } } } // 直線的に移動した時の距離を計算する pair<int, float> calc_length(int k, Point p, const Point &q, int th = EDGE_MAX_LEN) { float power = std::pow(1.1, k); float vx = q.x - p.x; float vy = q.y - p.y; float vlen = std::sqrt(vx * vx + vy * vy); vx /= vlen; vy /= vlen; float dist = vlen; for (int len = 1; len <= th; len++) { float coeff = tcoeff[(int)p.y][(int)p.x] * power; dist -= coeff; if (dist <= 0.0f) { return std::make_pair(len, -dist); } p.x += vx * coeff; p.y += vy * coeff; } return std::make_pair(250, 0.0f); } } // namespace calc_field } // namespace hpc #endif // ============================== // end of local/calc_length.hpp // ============================== // #include "local/graph.hpp" // ============================== // local/graph.hpp // ============================== #ifndef __GRAPH_HPP #define __GRAPH_HPP #include <utility> #include <vector> using std::pair; using std::vector; namespace hpc { using graph_type = vector<vector<vector<int>>>; graph_type new_graph(int n, int m, int initial_value = 0) { return graph_type(n, vector<vector<int>>(m, vector<int>(m, initial_value))); } using dist_type = pair<int, float>; using adjacent_graph_type = vector<vector<vector<pair<int, dist_type>>>>; adjacent_graph_type new_adjacent_graph(int n, int m) { return adjacent_graph_type(n, vector<vector<pair<int, dist_type>>>(m)); } } // namespace hpc #endif // ============================== // end of local/graph.hpp // ============================== // #include "local/graph_cons.hpp" // ============================== // local/graph_cons.hpp // ============================== #ifndef __GRAPH_CONS_HPP #define __GRAPH_CONS_HPP // #include "calc_length.hpp" // #include "graph.hpp" // #include "point.hpp" #include <cmath> #include <queue> #include <set> #include <utility> #include <vector> namespace hpc { using std::pair; using std::priority_queue; using std::vector; Terrain get_terrain(const Stage &stage, int x, int y) { return calc_field::terrain[y][x]; } bool diff_terrain(const Stage &stage, int ax, int ay, int bx, int by) { return get_terrain(stage, ax, ay) != get_terrain(stage, bx, by); } // 隣接グラフを生成する // 頂点の次数が大体 neighbor_size ぐらいになるようにする // 隣接グラフは各巻物数を考慮してそれぞれ計算される adjacent_graph_type create_neighbor_graph(const Stage &stage, const vector<Point> &pos, const int max_len, const int neighbor_size) { int n = stage.scrolls().count(); int m = (int)pos.size(); auto g = new_adjacent_graph(n, m); for (int i = 0; i < m; i++) { g[0][i].reserve(neighbor_size + 10); float power = 1.0; vector<pair<pair<Point, Point>, dist_type>> ps; ps.reserve(m - 1); for (int j = 0; j < m; j++) { if (j != i && abs(pos[i] - pos[j]) < GRAPH_PRUNE) { Point v = pos[j] - pos[i]; v = v / abs(v); ps.emplace_back(std::make_pair(pos[i], v), std::make_pair(j, abs(pos[j] - pos[i]))); } } Terrain oter = stage.terrain(pos[i]); for (int len = 1; len <= max_len; len++) { vector<pair<pair<Point, Point>, dist_type>> nxt; nxt.reserve(ps.size()); for (auto P : ps) { int j = P.second.first; float rest = P.second.second; Point p = P.first.first; Point v = P.first.second; float coeff = Parameter::JumpTerrianCoefficient[(int)stage.terrain(p)]; rest -= power * coeff; if (rest <= 0.0f) { g[0][i].emplace_back(j, std::make_pair(len, -rest)); } else if (len == 4 && stage.terrain(p) != oter) { continue; } else { p = p + v * (coeff * power); nxt.emplace_back(std::make_pair(p, v), std::make_pair(j, rest)); } } if ((int)g[0][i].size() >= neighbor_size) { break; } ps.swap(nxt); } } for (int k = 1; k < n; k++) { for (int i = 0; i < m; i++) { g[k][i].reserve(g[0][i].size()); for (auto P : g[0][i]) { int to = P.first; g[k][i].emplace_back(to, calc_field::calc_length(k, pos[i], pos[to])); } } } return g; } // namespace hpc } // namespace hpc #endif // ============================== // end of local/graph_cons.hpp // ============================== // #include "local/augment_points.hpp" // ============================== // local/augment_points.hpp // ============================== #ifndef __AUGMENT_POINTS_HPP #define __AUGMENT_POINTS_HPP // #include "config.hpp" // #include "point.hpp" #include <algorithm> #include <cmath> #include <vector> namespace hpc { using std::vector; // 各点から近くの線分に垂線を下ろした時の点を計算する vector<Point> get_augment_points(const Stage &stage, const vector<Point> &pos) { const static float dy[4] = {-1.0, 0.0, +1.0, 0.0}; const static float dx[4] = {0.0, +1.0, 0.0, -1.0}; vector<Point> ret; for (const Point &p : pos) { Terrain terrain = stage.terrain(p); if (terrain == Terrain::Plain) { continue; } for (int dir = 0; dir < 4; dir++) { Terrain t = terrain; Point q = p; bool onemore = true; while (true) { q.x += dx[dir] * AUG_STEP; q.y += dy[dir] * AUG_STEP; if (!in_stage(q)) { break; } Terrain t2 = stage.terrain(q); if (t2 > t) { q.x -= dx[dir] * AUG_STEP; q.y -= dy[dir] * AUG_STEP; if (get_nearest_distance(pos, q) > AUG_THRESHOLD) { ret.push_back(q); } if (onemore) { t = t2; onemore = false; } else { break; } } if (t2 < t) { if (get_nearest_distance(pos, q) > AUG_THRESHOLD) { ret.push_back(q); } t = t2; if (!onemore) { break; } } } } } return ret; } vector<Point> get_augment_points2(const Stage &stage, const vector<Point> &pos) { const static float dy[4] = {-1.0, 0.0, +1.0, 0.0}; const static float dx[4] = {0.0, +1.0, 0.0, -1.0}; vector<Point> ret; for (const Point &p : pos) { Terrain terrain = stage.terrain(p); if (terrain == Terrain::Plain) { continue; } for (int dir = 0; dir < 4; dir++) { Point v(dx[dir] * AUG_STEP, dy[dir] * AUG_STEP); Terrain t = terrain; Point q = p; bool onemore = true; while (true) { q = q + v; if (!in_stage(q)) { break; } Terrain t2 = stage.terrain(q); if (t2 > t) { q = q - v; if (get_nearest_distance(pos, q) > AUG_THRESHOLD) { ret.push_back(q); ret.push_back(q + v); } if (onemore) { t = t2; onemore = false; } else { break; } } if (t2 < t) { if (get_nearest_distance(pos, q) > AUG_THRESHOLD) { ret.push_back(q); ret.push_back(q - v); } t = t2; if (!onemore) { break; } } } } } return ret; } } // namespace hpc #endif // ============================== // end of local/augment_points.hpp // ============================== // #include "local/corners.hpp" // ============================== // local/corners.hpp // ============================== #ifndef __CORNERS_HPP #define __CORNERS_HPP // #include "point.hpp" #include <vector> namespace hpc { using std::vector; // フィールド上の「角」になる点を計算する // 「角」としては隣接する2x2マスの中に90度の角があるところを候補とする // 各「角」に対し,点の数を絞るため,より速い地形に点を置くようにする vector<Point> get_corners(const Stage &stage, float delta = 0.05, bool less = true) { vector<Point> ret; for (int x = 0; x < 49; x++) { for (int y = 0; y < 49; y++) { Point a(x + 1.0 - delta, y + 1.0 - delta); Point b(x + 1.0 + delta, y + 1.0 - delta); Point c(x + 1.0 + delta, y + 1.0 + delta); Point d(x + 1.0 - delta, y + 1.0 + delta); auto at = stage.terrain(a); auto bt = stage.terrain(b); auto ct = stage.terrain(c); auto dt = stage.terrain(d); bool corner = false; corner |= at != bt && bt != ct; corner |= bt != ct && ct != dt; corner |= ct != dt && dt != at; corner |= dt != at && at != bt; if (!corner) { continue; } for (int _ = 0; _ < 4; _++) { // check if (at == bt && bt == ct && ct != dt) { if (at < dt) { // abc:light, d:heavy ret.push_back(b); } else { // abc:heavy, d:light ret.push_back(d); } break; } else if (at == bt && at != ct && at != dt && ct != dt) { if (at < ct && at < dt) { // ab:light, c,d:heavy ret.push_back(ct < dt ? b : a); } else if (at > ct && at > dt) { // ab:heavy, c,d:light ret.push_back(ct < dt ? c : d); } else { // ab:middle if (ct < dt) { if (!less) { ret.push_back(b); } ret.push_back(c); } else { if (!less) { ret.push_back(a); } ret.push_back(d); } } break; } // swap std::swap(a, b); std::swap(b, c); std::swap(c, d); std::swap(at, bt); std::swap(bt, ct); std::swap(ct, dt); } } } return ret; } } // namespace hpc #endif // ============================== // end of local/corners.hpp // ============================== // #include "local/apsp.hpp" // ============================== // local/apsp.hpp // ============================== #ifndef __APSP_HPP #define __APSP_HPP // #include "calc_length.hpp" // #include "config.hpp" // #include "graph.hpp" // #include "point.hpp" #include <algorithm> #include <stack> #include <utility> #include <vector> namespace hpc { using std::vector; using paths_type = vector<vector<vector<vector<int>>>>; // 巻物(0 ~ n-1)と初期地点(n)の間の最短経路をそれぞれ求める // アルゴリズムは Dijkstra 法を高速にした Dial 法を使用している // また計算にはターン数の他に「移動時に余った距離」も考慮するようにしている std::pair<graph_type, paths_type> calc_apsp(int n, const vector<Point> &pos, const adjacent_graph_type &g) { int m = pos.size(); graph_type g_out = new_graph(n, n + 1, 1000); paths_type path_out( n, vector<vector<vector<int>>>(n + 1, vector<vector<int>>(n + 1))); // dijkstra vector<int> dist(m); vector<float> restlen(m, 0.0); vector<bool> visited(m); vector<int> bef(m); std::stack<int, vector<int>> que[APSP_MAX_LEN]; for (int kk = 0; kk < n; kk++) { for (int i = 0; i < n + 1; i++) { if (i == n && kk != 0) { for (int j = 0; j < n + 1; j++) { g_out[kk][i][j] = APSP_MAX_LEN; } continue; } // using Dial's algorithm // O(md + maxlen) std::fill(dist.begin(), dist.end(), APSP_MAX_LEN - 1); std::fill(restlen.begin(), restlen.end(), 0.0); std::fill(visited.begin(), visited.end(), false); std::fill(bef.begin(), bef.end(), -1); for (int q = 0; q < APSP_MAX_LEN; q++) { while (que[q].size()) { que[q].pop(); } } dist[i] = 0; que[0].push(i); for (int dial = 0; dial < APSP_MAX_LEN; dial++) { if (que[dial].size() == 0) { continue; } while (que[dial].size()) { int j = que[dial].top(); que[dial].pop(); if (visited[j]) { continue; } visited[j] = true; for (auto P : g[kk][j]) { int to = P.first; auto dd = P.second; int nd = dist[j] + dd.first; float nr = restlen[j] + dd.second; if (nd < dist[to] || (nd == dist[to] && nr > restlen[to])) { dist[to] = nd; restlen[to] = nr; bef[to] = j; que[nd].push(to); } } } // early break int visited_count = 0; for (int j = 0; j < n; j++) { visited_count += visited[j]; } if (visited_count == n || visited_count >= n*4/5 + 1) { break; } } for (int j = 0; j < n + 1; j++) { g_out[kk][i][j] = dist[j]; auto &v = path_out[kk][i][j]; v.clear(); if (dist[j] >= APSP_MAX_LEN - 1) { continue; } int last = j; v.push_back(last); while (last != i) { last = bef[last]; v.push_back(last); } std::reverse(v.begin(), v.end()); } } } return std::make_pair(g_out, path_out); } } // namespace hpc #endif // ============================== // end of local/apsp.hpp // ============================== // #include "local/atom_optimizer.hpp" // ============================== // local/atom_optimizer.hpp // ============================== #ifndef __ATOM_OPTIMIZER_HPP #define __ATOM_OPTIMIZER_HPP // #include "calc_length.hpp" // #include "config.hpp" // #include "point.hpp" #include <cmath> #include <stack> #include <vector> namespace hpc { using std::pair; using std::vector; // atom optimizer: 経路の点を少し揺らして最短経路が更新されるかを試す // corner_opt フラグが立ってる場合は十文字に,そうでない場合は円形に揺らす // 命名は揺らし方を原子(atom)の電子配置から思いついたため vector<Point> atom_optimize(const Stage &stage, const vector<Point> &origin, float deviate = 0.0, bool corner_opt = false, int round = 0) { // construct delta vector auto rot = [&](int j, int k, int n) { float th = 2 * M_PI / n * k + (ATOM_DEVIATE ? deviate * j : 0.0f); return Point(std::cos(th), std::sin(th)); }; vector<Point> delta; delta.push_back(Point(0, 0)); if (!corner_opt) { for (int i = 0; i < ATOM_NS[round % ATOM_NS.size()]; i++) { delta.push_back(ATOM_LENS[round % ATOM_LENS.size()] * rot(round + 1, i, ATOM_NS[round % ATOM_NS.size()])); } } else { for (float len = ATOM_CROSS_START; len < ATOM_CROSS_STOP; len += ATOM_CROSS_STEP) { for (int i = 0; i < 4; i++) { delta.push_back(len * rot(0, i, 4)); } } } int n = stage.scrolls().count(); int m = origin.size(); int u = delta.size(); using calc_field::calc_length; int scroll_count = 0; vector<bool> gotten(n, false); vector<Point> cands; vector<bool> valid; vector<int> cands_count; vector<int> scroll_ids; for (int pi = 0; pi < (int)origin.size(); pi++) { const Point &p = origin[pi]; int scroll_id = -1; for (int i = 0; i < n; i++) { if (!gotten[i] && static_cast<int>(stage.scrolls()[i].pos().x) == static_cast<int>(p.x) && static_cast<int>(stage.scrolls()[i].pos().y) == static_cast<int>(p.y)) { gotten[i] = true; scroll_count++; scroll_id = i; } } Terrain oter = stage.terrain(p); float power = std::pow(1.1, scroll_count); for (const Point &d : delta) { auto q = clamp_into_stage(p + power * d); cands.push_back(q); cands_count.push_back(scroll_count); scroll_ids.push_back( scroll_id != -1 && static_cast<int>(stage.scrolls()[scroll_id].pos().x) == static_cast<int>(q.x) && static_cast<int>(stage.scrolls()[scroll_id].pos().y) == static_cast<int>(q.y) ? scroll_id : -1); valid.push_back(oter == stage.terrain(q)); } } vector<vector<pair<int, pair<int, float>>>> g(cands.size()); for (int i = 0; i < (int)cands.size(); i++) { if (!valid[i] && ATOM_STRICT) { continue; } const Point &p = cands[i]; int k = cands_count[i]; int b = i / u; for (int j = 0; j < u; j++) { int to = u * b + j; if (!valid[to] && ATOM_STRICT) { continue; } auto len = calc_length(k, p, cands[to]); if (len.first > 0) { g[i].emplace_back(to, len); } } for (int j = 0; j < u; j++) { int to = u * (b + 1) + j; if (!valid[to] && ATOM_STRICT) { continue; } if (to < (int)cands.size()) { if (k == cands_count[to] || (k + 1 == cands_count[to] && scroll_ids[to] != -1)) { g[i].emplace_back(to, calc_length(k, p, cands[to])); } } } // skipper if (ATOM_SKIP) { for (int j = 0; j < u; j++) { int to = u * (b + 2) + j; if (!valid[to] && ATOM_STRICT) { continue; } if (to < (int)cands.size()) { if (k == cands_count[to] || (k + 1 == cands_count[to] && scroll_ids[to] != -1)) { g[i].emplace_back(to, calc_length(k, p, cands[to])); } } } } } // using Dial's algorithm // O(md + maxlen) m = cands.size(); vector<int> dist(m, ATOM_MAX_LEN - 1); vector<float> restlen(m, 0.0); vector<bool> visited(m, false); vector<int> bef(m, -1); std::stack<int, vector<int>> que[ATOM_MAX_LEN]; dist[0] = 0; que[0].push(0); for (int dial = 0; dial < ATOM_MAX_LEN; dial++) { if (que[dial].size() == 0) { continue; } bool goal = false; int goal_pos = -1; float best_nr = 0.0; while (que[dial].size()) { int j = que[dial].top(); que[dial].pop(); if (visited[j]) { continue; } visited[j] = true; if (cands_count[j] == n) { // goal goal = true; if (restlen[j] > best_nr) { goal_pos = j; best_nr = restlen[j]; } } for (auto nei : g[j]) { int to = nei.first; int nd = dist[j] + nei.second.first; float nr = restlen[j]; // if (scroll_ids[to] != -1) { nr += nei.second.second; // } if (nd < dist[to] || (nd == dist[to] && nr > restlen[to])) { dist[to] = nd; restlen[to] = nr; bef[to] = j; que[nd].push(to); } } } if (goal) { vector<Point> ret; int last = goal_pos; ret.push_back(cands[last]); while (last != 0) { last = bef[last]; if (abs(ret.back() - cands[last]) > 1e-8) { ret.push_back(cands[last]); } } std::reverse(ret.begin(), ret.end()); return ret; } } // fail return origin; } } // namespace hpc #endif // ============================== // end of local/atom_optimizer.hpp // ============================== // #include "local/tsp.hpp" // ============================== // local/tsp.hpp // ============================== #ifndef __TSP_HPP #define __TSP_HPP // #include "graph.hpp" #include <algorithm> #include <cassert> #include <fstream> #include <iomanip> #include <numeric> #include <random> #include <sstream> #include <vector> namespace hpc { int calc_score(int n, const graph_type &g, const std::vector<int> &order) { assert(order[0] == n); int ret = 0; for (int k = 0; k < n; k++) { int p = order[k]; int q = order[k + 1]; ret += g[k][p][q]; } return ret; } // 最近傍法によるTSP計算 std::vector<int> nearest_neighbor(int n, const graph_type &g) { std::vector<bool> visited(n, false); std::vector<int> order(n + 1); order[0] = n; for (int k = 0; k < n; k++) { int bef = order[k]; int to = -1; for (int i = 0; i < n; i++) { if (visited[i]) { continue; } if (to == -1 || g[k][bef][i] < g[k][bef][to]) { to = i; } } order[k + 1] = to; visited[to] = true; } return order; } // 3-opt 法によるTSP計算 std::vector<int> three_opt(int n, const graph_type &g, std::vector<int> order) { int score = calc_score(n, g, order); while (true) { bool updated = false; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n + 1; j++) { for (int k = j; k <= n + 1; k++) { auto tmp = order; int tmp_score = 0; // l std::reverse(tmp.begin() + i, tmp.begin() + j); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // r std::reverse(tmp.begin() + j, tmp.begin() + k); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // lr std::reverse(tmp.begin() + i, tmp.begin() + k); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // l std::reverse(tmp.begin() + i, tmp.begin() + j); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // r std::reverse(tmp.begin() + j, tmp.begin() + k); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // l std::reverse(tmp.begin() + i, tmp.begin() + j); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } // lr std::reverse(tmp.begin() + i, tmp.begin() + k); tmp_score = calc_score(n, g, tmp); if (tmp_score < score) { score = tmp_score; order = tmp; updated = true; } } } } if (!updated) { break; } } return order; } // 局所最適解からの脱出用メソッド(キック) template <typename RNG> std::vector<int> bridge_kick(int n, const graph_type &g, std::vector<int> order, RNG &rng) { for (int _ = 0; _ < 2; _++) { int i = rng() % n + 1; int j = rng() % (n - 1) + 1; if (j >= i) { j++; } else { std::swap(i, j); } std::reverse(order.begin() + i, order.begin() + j + 1); } return order; } // 最近傍法・3-opt・キックによるTSP計算 std::vector<int> nn_three_opt(int n, const graph_type &g) { assert((int)g.size() == n && (int)g[0].size() == n + 1 && (int)g[0][0].size() == n + 1); if (n <= 8) { std::vector<int> order(n + 1); order[0] = n; std::iota(order.begin() + 1, order.end(), 0); std::vector<int> best_order = order; int best_score = 1000; do { int score = calc_score(n, g, order); if (score < best_score) { best_order = order; best_score = score; } } while (std::next_permutation(order.begin() + 1, order.end())); return best_order; } // 1. Nearest Neighbor auto order = nearest_neighbor(n, g); // 2. 3-opt order = three_opt(n, g, order); // 3. kick and 3-opt auto best_order = order; int best_score = calc_score(n, g, order); std::mt19937 rng(252521); for (int i = 0; i < 30; i++) { order = bridge_kick(n, g, order, rng); order = three_opt(n, g, order); int score = calc_score(n, g, order); if (score < best_score) { best_score = score; best_order = order; } } return best_order; } } // namespace hpc #endif // ============================== // end of local/tsp.hpp // ============================== namespace hpc { //------------------------------------------------------------------------------ // constructor / destructor int stage_count; int stage_turn; int total_turn; Answer::Answer() { stage_count = 0; stage_turn = 0; total_turn = 0; } Answer::~Answer() { fprintf(stderr, "total turn: %d\n", total_turn); } //------------------------------------------------------------------------------ // stage begin // 移動経路を記述する点のリストと,その順序を格納する配列 vector<Point> pos; vector<int> order; // std::vector<Vector2> Answer::initialize(const Stage &aStage) { void Answer::initialize(const Stage &aStage) { // 初期化 order.clear(); pos.clear(); int n = aStage.scrolls().count(); calc_field::initialize_length_field(aStage); // 候補点群の計算(巻物,初期地点,角,補助点) for (int i = 0; i < n; i++) { pos.push_back(aStage.scrolls()[i].pos()); } pos.push_back(aStage.rabbit().pos()); auto corners = get_corners(aStage, CORNER_DELTA, CORNER_LESS); pos.insert(pos.end(), corners.begin(), corners.end()); auto augments = get_augment_points2(aStage, pos); pos.insert(pos.end(), augments.begin(), augments.end()); // デバッグ用: SPECIFIC に指定されたステージ以外では切り上げ if (stage_count != SPECIFIC && SPECIFIC >= 0) { order.clear(); for (int i = 0; i < n; i++) { order.push_back(i); } // return pos; return; } // 候補点群を線分で結ぶ有向グラフの構築 int m = pos.size(); auto g = create_neighbor_graph(aStage, pos, APSP_MAX_LEN, APSP_NEIGHBOR); // 有向グラフの逆辺の追加 vector<vector<int>> rg(m); for (int i = 0; i < m; i++) { for (auto P : g[0][i]) { rg[P.first].push_back(i); } } for (int k = 0; k < n; k++) { for (int i = 0; i < m; i++) { g[k][i].reserve(g[k][i].size() + rg[i].size()); for (int j : rg[i]) { g[k][i].emplace_back(j, calc_field::calc_length(k, pos[i], pos[j])); } } } // 有向グラフにおける巻物間の最短経路計算 // 最短経路長の表とその際に通る点群のリストを返す auto apsp_out = calc_apsp(n, pos, g); auto g2 = apsp_out.first; auto path = apsp_out.second; // 巡回セールスマン問題による最短経路計算 // 初期地点から始まってすべての巻物を回収する最短経路 auto tsp_order = nn_three_opt(n, g2); // 実際に通る点群を計算 order.clear(); order.push_back(n); for (int kk = 0; kk < n; kk++) { int from = tsp_order[kk]; int to = tsp_order[kk + 1]; auto &v = path[kk][from][to]; for (int i = 1; i < (int)v.size(); i++) { order.push_back(v[i]); } } // 経路の最適化 vector<Point> points; for (int i : order) { points.push_back(pos[i]); } for (int i = 0; i < ATOM_OPT_NUM; i++) { points = atom_optimize(aStage, points, i, ATOM_CROSS[i], i); } // 整理 pos = points; order.clear(); for (int i = 0; i < (int)points.size(); i++) { order.push_back(i); } // スタック構造として使用するために順序を逆転させる std::reverse(order.begin(), order.end()); // return pos; return; } Vector2 Answer::getTargetPos(const Stage &aStage) { stage_turn++; total_turn++; // エラー if (order.size() == 0) { fprintf(stderr, "order is null!\n"); return aStage.rabbit().pos(); } // 指定された点に向けて移動する auto &p = aStage.rabbit().pos(); auto &q = pos[order.back()]; // 指定された点に到達していたらスタックからポップする if (abs(p - q) < 1e-8) { order.pop_back(); } return pos[order.back()]; } void Answer::finalize(const Stage &aStage) { if (SPECIFIC == -1 || SPECIFIC == stage_count) { fprintf(stderr, "stage %3d: %3d\n", stage_count, stage_turn); } stage_count++; stage_turn = 0; } } // namespace hpc // EOF