nariさんの作品(ソースコード)

//------------------------------------------------------------------------------
/// @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