結果

問題 No.5015 Escape from Labyrinth
ユーザー Jiro_tech15Jiro_tech15
提出日時 2023-04-15 17:50:59
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 2,838 ms / 3,000 ms
コード長 23,603 bytes
コンパイル時間 5,069 ms
コンパイル使用メモリ 224,392 KB
実行使用メモリ 167,684 KB
スコア 170,360
最終ジャッジ日時 2023-04-15 17:55:46
合計ジャッジ時間 286,436 ms
ジャッジサーバーID
(参考情報)
judge15 / judge16
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2,676 ms
132,288 KB
testcase_01 AC 2,696 ms
135,496 KB
testcase_02 AC 2,742 ms
144,280 KB
testcase_03 AC 2,690 ms
129,252 KB
testcase_04 AC 2,678 ms
117,132 KB
testcase_05 AC 2,733 ms
150,260 KB
testcase_06 AC 2,713 ms
140,756 KB
testcase_07 AC 2,704 ms
125,848 KB
testcase_08 AC 2,696 ms
136,060 KB
testcase_09 AC 2,743 ms
142,692 KB
testcase_10 AC 2,699 ms
138,996 KB
testcase_11 AC 2,698 ms
139,408 KB
testcase_12 AC 2,698 ms
126,896 KB
testcase_13 AC 2,813 ms
167,684 KB
testcase_14 AC 2,831 ms
162,336 KB
testcase_15 AC 2,698 ms
140,652 KB
testcase_16 AC 2,725 ms
145,116 KB
testcase_17 AC 2,723 ms
145,080 KB
testcase_18 AC 2,723 ms
138,760 KB
testcase_19 AC 2,696 ms
133,580 KB
testcase_20 AC 2,688 ms
138,860 KB
testcase_21 AC 2,744 ms
146,140 KB
testcase_22 AC 2,689 ms
133,412 KB
testcase_23 AC 2,666 ms
132,472 KB
testcase_24 AC 2,706 ms
137,428 KB
testcase_25 AC 2,662 ms
131,668 KB
testcase_26 AC 2,657 ms
126,144 KB
testcase_27 AC 2,745 ms
153,432 KB
testcase_28 AC 2,737 ms
134,224 KB
testcase_29 AC 2,733 ms
146,780 KB
testcase_30 AC 2,716 ms
141,668 KB
testcase_31 AC 2,657 ms
128,384 KB
testcase_32 AC 2,736 ms
146,528 KB
testcase_33 AC 2,694 ms
131,336 KB
testcase_34 AC 2,760 ms
143,848 KB
testcase_35 AC 2,745 ms
152,168 KB
testcase_36 AC 2,751 ms
147,980 KB
testcase_37 AC 2,815 ms
164,904 KB
testcase_38 AC 2,798 ms
156,988 KB
testcase_39 AC 2,756 ms
142,184 KB
testcase_40 AC 2,712 ms
138,396 KB
testcase_41 AC 2,702 ms
139,448 KB
testcase_42 AC 2,746 ms
148,608 KB
testcase_43 AC 2,698 ms
130,768 KB
testcase_44 AC 2,766 ms
146,196 KB
testcase_45 AC 2,716 ms
152,848 KB
testcase_46 AC 2,748 ms
146,448 KB
testcase_47 AC 2,707 ms
138,100 KB
testcase_48 AC 2,794 ms
153,296 KB
testcase_49 AC 2,731 ms
139,592 KB
testcase_50 AC 2,681 ms
137,184 KB
testcase_51 AC 2,718 ms
137,184 KB
testcase_52 AC 2,736 ms
140,596 KB
testcase_53 AC 2,728 ms
139,056 KB
testcase_54 AC 2,765 ms
149,476 KB
testcase_55 AC 2,734 ms
145,516 KB
testcase_56 AC 2,767 ms
151,192 KB
testcase_57 AC 2,744 ms
144,320 KB
testcase_58 AC 2,769 ms
141,232 KB
testcase_59 AC 2,722 ms
132,540 KB
testcase_60 AC 2,700 ms
138,460 KB
testcase_61 AC 2,695 ms
134,352 KB
testcase_62 AC 2,701 ms
135,416 KB
testcase_63 AC 2,732 ms
140,804 KB
testcase_64 AC 2,742 ms
136,300 KB
testcase_65 AC 2,715 ms
141,756 KB
testcase_66 AC 2,717 ms
137,488 KB
testcase_67 AC 2,739 ms
142,480 KB
testcase_68 AC 2,720 ms
137,708 KB
testcase_69 AC 2,752 ms
143,788 KB
testcase_70 AC 2,734 ms
151,420 KB
testcase_71 AC 2,724 ms
150,848 KB
testcase_72 AC 2,713 ms
139,576 KB
testcase_73 AC 2,723 ms
143,744 KB
testcase_74 AC 2,736 ms
141,768 KB
testcase_75 AC 2,683 ms
136,368 KB
testcase_76 AC 2,782 ms
161,328 KB
testcase_77 AC 2,716 ms
134,636 KB
testcase_78 AC 2,785 ms
153,224 KB
testcase_79 AC 2,717 ms
134,256 KB
testcase_80 AC 2,727 ms
139,852 KB
testcase_81 AC 2,645 ms
119,440 KB
testcase_82 AC 2,659 ms
127,808 KB
testcase_83 AC 2,692 ms
132,328 KB
testcase_84 AC 2,774 ms
153,928 KB
testcase_85 AC 2,668 ms
130,592 KB
testcase_86 AC 2,732 ms
149,696 KB
testcase_87 AC 2,716 ms
141,636 KB
testcase_88 AC 2,742 ms
142,212 KB
testcase_89 AC 2,744 ms
144,312 KB
testcase_90 AC 2,689 ms
134,840 KB
testcase_91 AC 2,670 ms
127,644 KB
testcase_92 AC 2,838 ms
165,360 KB
testcase_93 AC 2,782 ms
158,276 KB
testcase_94 AC 2,749 ms
149,872 KB
testcase_95 AC 2,718 ms
139,468 KB
testcase_96 AC 2,714 ms
145,244 KB
testcase_97 AC 2,727 ms
145,204 KB
testcase_98 AC 2,759 ms
148,324 KB
testcase_99 AC 2,741 ms
146,552 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
main.cpp: メンバ関数 ‘std::vector<Pos> Solver::Solve(int)’ 内:
main.cpp:927:3: 警告: 制御が非 void 関数の終りに到達しました [-Wreturn-type]
  927 |   }
      |   ^
main.cpp: 静的メンバ関数 ‘static void Pos::StaticInit(int)’ 内:
main.cpp:390:24: 警告: ‘dir’ may be used uninitialized [-Wmaybe-uninitialized]
  390 |             move_to[dir][p] = {adj_x, adj_y};
      |                        ^
main.cpp:377:17: 備考: ‘dir’ はここで宣言されています
  377 |             Dir dir;
      |                 ^~~

ソースコード

diff #

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]);
  }
}






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),
        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) {
    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 % 10000 == 0) {
      //   cout << sol.Eval() << " " << sol.Score() << endl;
      //   cout << sol.Convert() << endl << endl;
      // }
      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 prev_sol = sol;
      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 {
        // const auto mid_sol = sol;
        nbd->Restore(&sol);
        // if (prev_sol != sol) {
        //   for (const auto& obj : prev_sol.pos_pairs) {
        //     cerr << obj.Idx() << " ";
        //   }
        //   for (const auto& obj : mid_sol.pos_pairs) {
        //     cerr << obj.Idx() << " ";
        //   }
        //   for (const auto& obj : sol.pos_pairs) {
        //     cerr << obj.Idx() << " ";
        //   }
        //   abort();
        // }
      }
    }
    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_.ms(); }
  void UpdateTemp() {
    cur_temp_ = max(0.0, start_temp_ - cur_time_ * diff_temp_ * inv_time_);
  }
};



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());
  }
  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 */

template <typename Point, typename Dist>
class Graph_T {
 public:
  struct Edge {
    Point to;
    Dist dist;

    Edge(const Point _to, const Dist _dist) : to(_to), dist(_dist) {}
  };
  explicit Graph_T(const int n) : n_(n), adjs_(n) {}
  int N() const { return n_; }
  void AddEdge(const Point a, const Point b, const Dist dist) {
    adjs_[a].emplace_back(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; });
      adjs_[a].erase(itr);
    }
  }

 private:
  int n_;
  vector<vector<Edge>> adjs_;
};

template <typename Point, typename Dist>
struct DijkstraData {
  Dist dist = 1e9;
  Point prev;
};

template <class Graph, typename Point, typename Dist>
vector<DijkstraData<Point, Dist>> Dijkstra(const Graph& graph,
                                           const Point& base_point) {
  vector<DijkstraData<Point, Dist>> datas(graph.N());
  using P = tuple<Dist, Point, Point>;
  priority_queue<P, vector<P>, greater<P>> pq;

  datas[base_point] = {0, base_point};
  pq.emplace(0, base_point, base_point);
  while (pq.size() > 0) {
    const auto [dist, point, prev_point] = pq.top();
    pq.pop();
    if (datas[point].dist < dist) continue;
    for (const auto& edge : graph.Adj(point)) {
      const Point to = edge.to;
      const Dist edge_dist = edge.dist;
      const Dist new_dist = dist + edge_dist;
      if (datas[to].dist > new_dist) {
        datas[to].dist = new_dist;
        datas[to].prev = point;
        pq.emplace(new_dist, to, point);
      }
    }
  }
  return datas;
}

template <typename Point, typename Dist>
vector<Point> Keiro(const vector<DijkstraData<Point, Dist>>& dijkstra_results,
                    const Point& from, const Point& to) {
  // 到達可能であることは仮定
  vector<Point> ret;
  Point p = to;
  while (p != from) {
    ret.emplace_back(p);
    p = dijkstra_results[p].prev;
  }

  ret.emplace_back(from);

  reverse(all(ret));
  return ret;
}

/* start */

struct Board {
  using Graph = Graph_T<Pos, float>;
  Board() {}
  Board(istream& is) {
    is >> N >> D >> H;
    Pos::StaticInit(N);
    rep(i, N) {
      string s;
      is >> s;
      S += s;
    }
    is >> M;
    rep(i, M) {
      int y, x, d;
      is >> y >> x >> d;
      Tanchiki tanchiki{Pos(x, y), d};
      tanchikies_.emplace_back(tanchiki);
    }

    rep(idx, Pos::N2) {
      const Pos p(idx);
      if (S[p] == 'S') {
        start_pos = p;
      } else if (S[p] == 'G') {
        goal_pos = p;
      } else if (S[p] == 'K') {
        key_pos = p;
      } else if (S[p] == 'J') {
        jewel_count++;
      }

      if (IsCheckPoint(p)) {
        check_points.emplace_back(p);
      }
    }

    InitBeamCount();
    InitDist();
  }

  void InitBeamCount() {
    pm2beams_.resize(Pos::N2, vector<int>(60));
    for (const auto& tanchiki : tanchikies_) {
      for (const auto dir : {kU, kD, kL, kR}) {
        Pos p = tanchiki.p;
        while (p.Move(dir) && CanMove(p)) {
          rep(t, 60) {
            if (t % tanchiki.d == 0) {
              pm2beams_[p][t]++;
            }
          }
        }
      }
    }
  }

  void InitDist() {
    Graph graph(Pos::N2);
    rep(idx1, Pos::N2) {
      const Pos p1(idx1);
      if (!CanMove(p1)) continue;
      int acc_p1 = std::accumulate(all(pm2beams_[p1]), 0);
      for (const auto& p2 : p1.Adj()) {
        if (!CanMove(p2)) continue;
        const float expected_hps =
            (acc_p1 + std::accumulate(all(pm2beams_[p2]), 0)) * D * 0.7 /
                120.0 +
            1.0;
        graph.AddEdge(p1, p2, expected_hps);
      }
    }

    dists_.resize(Pos::N2);
    keiros_.resize(Pos::N2);

    rep(idx1, Pos::N2) {
      const Pos p1(idx1);
      if (!IsCheckPoint(p1)) continue;
      dists_[idx1].resize(Pos::N2, INF);
      keiros_[idx1].resize(Pos::N2);

      const auto ret = Dijkstra<Graph, Pos, float>(graph, p1);
      rep(idx2, Pos::N2) {
        const Pos p2(idx2);
        if (!IsCheckPoint(p2)) continue;

        const auto& [dist, _] = ret[idx2];
        dists_[idx1][idx2] = dist;
        if (dist < INF * 0.1) {
          keiros_[idx1][idx2] = ::Keiro(ret, p1, p2);
        }
      }
    }
  }

  bool IsCheckPoint(const Pos& p) const {
    // TODO: Fも追加
    return S[p] == 'S' || S[p] == 'G' || S[p] == 'K' || S[p] == 'J';
  }

  int BeamCount(const Pos& p, const int t) const {
    return pm2beams_[p][t % 60];
  }

  bool CanMove(const Pos& p) const {
    return S[p] != '#' && S[p] != 'B' && S[p] != 'E';
  }

  float Dist(const Pos& p1, const Pos& p2) const { return dists_[p1][p2]; }

  const vector<Pos>& Keiro(const Pos& from, const Pos& to) const {
    return keiros_[from][to];
  }

  // 消費したHPを返す
  int Simulate(const vector<Pos>& poses, int t) const {
    // posesはSとGも含む
    int hp = (int)poses.size() - 1;
    reps(i, (int)poses.size() - 1) {
      const auto& p = poses[i];
      hp += pm2beams_[p][(t + i) % 60] * D;
    }
    return hp;
  }

  // 消費したHPをDPで最小化してから、返す
  pair<vector<Pos>, int> DPSimulate(const vector<Pos>& poses, int t,
                                    const int limit_hp) const {
    // posesはSとGも含む
    struct Data {
      int hp = INF;
      int prev_i;
      int prev_j;
    };
    // dp[i][j] : poses[i]の行動が終わっていて、t%60がj (次のt%60はj+1)
    vector<vector<Data>> dp(poses.size(), vector<Data>(60));
    dp[0][t % 60].hp = 0;
    rep(i, (int)poses.size() - 1) {
      const Pos& p = poses[i];
      const Pos& next_p = poses[i + 1];
      rep(_, 2) {
        // トーラスなので2週する
        rep(t, 60) {
          const int hp = dp[i][t].hp;
          const int next_time = (t + 1) % 60;

          // 移動する
          {
            auto& next_data = dp[i + 1][next_time];
            const int next_hp = hp + pm2beams_[next_p][next_time] * D + 1;
            if (next_hp < next_data.hp) {
              next_data.hp = next_hp;
              next_data.prev_i = i;
              next_data.prev_j = t;
            }
          }

          // その場にとどまる
          {
            auto& next_data = dp[i][next_time];
            const int next_hp = hp + pm2beams_[p][next_time] * D + 1;
            if (next_hp < next_data.hp) {
              next_data.hp = next_hp;
              next_data.prev_i = i;
              next_data.prev_j = t;
            }
          }
        }
      }
    }

    int best_hp = INF;
    int best_i = (int)dp.size() - 1;
    int best_j = 0;

    rep(t, 60) {
      if (dp[best_i][t].hp < best_hp) {
        best_hp = dp[best_i][t].hp;
        best_j = t;
      }
    }

    if (best_hp > limit_hp) {
      return {{}, INF};
    }

    vector<Pos> ret;
    ret.reserve(poses.size() * 2);
    int i = best_i, j = best_j;
    while (i != t % 60 || j != 0) {
      ret.emplace_back(poses[i]);
      const int prev_i = dp[i][j].prev_i;
      const int prev_j = dp[i][j].prev_j;
      i = prev_i;
      j = prev_j;
    }
    ret.emplace_back(poses[i]);
    reverse(all(ret));
    return {ret, best_hp};
  }

  static vector<Pos> Merge(const vector<vector<Pos>>& keiros) {
    assert(keiros.size() > 0);
    vector<Pos> ret;
    for (const auto& kerio : keiros) {
      assert(kerio.size() > 0);
      ret.insert(ret.end(), kerio.begin(),
                 kerio.begin() + ((int)kerio.size() - 1));
    }
    ret.emplace_back(keiros.back().back());
    return ret;
  }

  // public

  int N, D, H, M;
  string S;

  Pos start_pos, goal_pos, key_pos;
  int jewel_count = 0;

  // private
  struct Tanchiki {
    Pos p;
    int d;
  };
  vector<vector<float>> dists_;
  vector<vector<vector<Pos>>> keiros_;
  vector<Tanchiki> tanchikies_;
  vector<Pos> check_points;

  // [pos][mod]
  vector<vector<int>> pm2beams_;
};

ostream& operator<<(ostream& os, const vector<Pos> keiro) {
  rep(i, (int)keiro.size() - 1) {
    const Pos from = keiro[i];
    const Pos to = keiro[i + 1];
    if (from == to) {
      os << "S";
    } else if (from.Y() - 1 == to.Y()) {
      os << "M U";
    } else if (from.Y() + 1 == to.Y()) {
      os << "M D";
    } else if (from.X() - 1 == to.X()) {
      os << "M L";
    } else if (from.X() + 1 == to.X()) {
      os << "M R";
    }
    if (i != (int)keiro.size() - 2) {
      os << "\n";
    }
  }
  return os;
}

/* start */

Board global_board;

struct Individual {
  vector<Pos> points;
  float hp = INF;
  bitset<3600> exists = 0;

  float DiffInsert(const Board& board, const int idx, const Pos& pos) const {
    assert(idx >= 1);
    assert(idx < (int)points.size());
    return board.Dist(points[idx - 1], pos) + board.Dist(pos, points[idx]);
  }

  void Insert(const Board& board, const int idx, const Pos& pos) {
    hp += DiffInsert(board, idx, pos);
    points.insert(points.begin() + idx, pos);
    exists[pos] = true;
  }

  float DiffSwap(const Board& board, int l, int r) const {
    assert(l < r);
    assert(0 < l);
    assert(r < (int)points.size());

    const float prev_dist = board.Dist(points[l - 1], points[l]) +
                            board.Dist(points[r - 1], points[r]);
    // TODO: 無向辺に直す
    const float next_dist = board.Dist(points[l - 1], points[r - 1]) +
                            board.Dist(points[l], points[r]);

    return next_dist - prev_dist;
  }

  void Swap(const Board& board, int l, int r) {
    hp += DiffSwap(board, l, r);
    reverse(points.begin() + l, points.begin() + r);
  }
};

struct Population {
  vector<Individual> inds;

  Population(const Board& board) : inds(board.jewel_count + 2) {
    inds[3].points =
        vector<Pos>{board.start_pos, board.key_pos, board.goal_pos};
    inds[3].hp = board.Dist(board.start_pos, board.key_pos) +
                 board.Dist(board.key_pos, board.goal_pos);
    inds[3].exists[board.start_pos] = true;
    inds[3].exists[board.key_pos] = true;
    inds[3].exists[board.goal_pos] = true;
  }
};

struct NBD {
  float Diff() const { return diff_; }
  virtual void Update(Population*) const = 0;
  void Restore(Population*) const {}

  float diff_;
};

struct NBD_insert : public NBD {
  NBD_insert() {}
  NBD_insert(Population* const sol, const int ind_idx, const int insert_idx,
             const Pos& p) {
    ind_idx_ = ind_idx;
    insert_idx_ = insert_idx;
    p_ = p;
    const auto& ind = sol->inds[ind_idx];
    assert(!ind.exists[p]);
    diff_ = ind.DiffInsert(global_board, insert_idx, p) + ind.hp -
            sol->inds[ind_idx + 1].hp;
  }
  void Update(Population* sol) const override {
    auto ind = sol->inds[ind_idx_];
    ind.Insert(global_board, insert_idx_, p_);
    sol->inds[ind_idx_ + 1] = ind;
  }

  int ind_idx_;
  int insert_idx_;
  Pos p_;
};

struct NBD_swap : public NBD {
  NBD_swap() {}
  NBD_swap(Population* const sol, const int ind_idx, const int l, const int r) {
    ind_idx_ = ind_idx;
    l_ = l;
    r_ = r;
    const auto& ind = sol->inds[ind_idx];
    diff_ = ind.DiffSwap(global_board, l, r);
  }
  void Update(Population* sol) const override {
    sol->inds[ind_idx_].Swap(global_board, l_, r_);
  }

  int ind_idx_;
  int l_;
  int r_;
};

struct NBDGenerator {
  NBDGenerator() {}
  NBD* Generate(Population* const sol, const int g) {
    while (true) {
      int ind_idx = rand(0, sol->inds.size());
      while (sol->inds[ind_idx].hp == INF) {
        ind_idx = rand(0, sol->inds.size());
      }

      auto& ind = sol->inds[ind_idx];

      if (rand(0, 100) < 30 || ind.hp < global_board.H) {
        // insert
        const int insert_idx = rand(1, ind.points.size());
        const Pos p = rand_vec(global_board.check_points);
        if (ind.exists[p]) continue;
        nbd_insert0 = NBD_insert(sol, ind_idx, insert_idx, p);
        return &nbd_insert0;
      } else {
        // swap
        const int r = rand(2, ind.points.size());
        const int l = rand(1, r);
        nbd_swap0 = NBD_swap(sol, ind_idx, l, r);
        return &nbd_swap0;
      }
    }
  }

  NBD_insert nbd_insert0;
  NBD_swap nbd_swap0;
};

class Solver {
 public:
  explicit Solver(istream& is) : board_(is) { global_board = board_; }
  vector<Pos> Solve(int time_limit) {
    Timer timer;
    timer.start();

    NBDGenerator nbd_generator;
    time_limit -= timer.ms();

    SimulatedAnnealing<Population, NBDGenerator> sa(time_limit - 500, 0.3, 0.1);
    Population initial_pop(board_);
    auto best_pop = sa.run(initial_pop, nbd_generator);

    REP(i, best_pop.inds.size()) {
      vector<vector<Pos>> keiros;
      const auto& ind = best_pop.inds[i];
      if (ind.hp == INF) continue;
      rep(j, (int)ind.points.size() - 1) {
        auto keiro = board_.Keiro(ind.points[j], ind.points[j + 1]);
        keiros.emplace_back(std::move(keiro));
      }

      const auto ans_no_dp = Board::Merge(keiros);

      const auto& [ans, hp] = board_.DPSimulate(ans_no_dp, 0, board_.H - 1);
      if (hp != INF) {
        cerr << "Jewel Count: " << (int)ind.points.size() - 3 << "/"
             << board_.jewel_count << endl;
        return ans;
      }
    }

    assert(false);
  }

 private:
  Board board_;
};

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);
  const auto ans = solver.Solve(2950 - timer.ms());
  cout << ans << endl;

  cerr << "ms: " << timer.ms() << endl;

  return 0;
}
0