結果

問題 No.5015 Escape from Labyrinth
ユーザー Jiro_tech15Jiro_tech15
提出日時 2023-04-15 19:30:27
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 2,611 ms / 3,000 ms
コード長 24,343 bytes
コンパイル時間 5,316 ms
コンパイル使用メモリ 233,580 KB
実行使用メモリ 166,384 KB
スコア 221,490
最終ジャッジ日時 2023-04-15 19:34:58
合計ジャッジ時間 270,123 ms
ジャッジサーバーID
(参考情報)
judge16 / judge11
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2,543 ms
131,888 KB
testcase_01 AC 2,568 ms
134,880 KB
testcase_02 AC 2,580 ms
143,252 KB
testcase_03 AC 2,567 ms
128,496 KB
testcase_04 AC 2,564 ms
116,704 KB
testcase_05 AC 2,564 ms
149,476 KB
testcase_06 AC 2,574 ms
139,596 KB
testcase_07 AC 2,555 ms
125,416 KB
testcase_08 AC 2,577 ms
135,412 KB
testcase_09 AC 2,586 ms
141,788 KB
testcase_10 AC 2,545 ms
138,464 KB
testcase_11 AC 2,565 ms
138,408 KB
testcase_12 AC 2,562 ms
125,984 KB
testcase_13 AC 2,605 ms
166,384 KB
testcase_14 AC 2,611 ms
161,068 KB
testcase_15 AC 2,556 ms
139,688 KB
testcase_16 AC 2,560 ms
144,200 KB
testcase_17 AC 2,569 ms
144,440 KB
testcase_18 AC 2,571 ms
138,060 KB
testcase_19 AC 2,568 ms
132,936 KB
testcase_20 AC 2,547 ms
137,588 KB
testcase_21 AC 2,586 ms
145,080 KB
testcase_22 AC 2,561 ms
132,300 KB
testcase_23 AC 2,568 ms
131,772 KB
testcase_24 AC 2,571 ms
136,536 KB
testcase_25 AC 2,539 ms
130,860 KB
testcase_26 AC 2,546 ms
125,556 KB
testcase_27 AC 2,581 ms
152,528 KB
testcase_28 AC 2,564 ms
133,312 KB
testcase_29 AC 2,581 ms
145,668 KB
testcase_30 AC 2,565 ms
140,864 KB
testcase_31 AC 2,559 ms
127,528 KB
testcase_32 AC 2,573 ms
145,472 KB
testcase_33 AC 2,571 ms
130,376 KB
testcase_34 AC 2,588 ms
142,784 KB
testcase_35 AC 2,565 ms
150,924 KB
testcase_36 AC 2,587 ms
146,968 KB
testcase_37 AC 2,599 ms
163,260 KB
testcase_38 AC 2,599 ms
155,960 KB
testcase_39 AC 2,591 ms
141,168 KB
testcase_40 AC 2,559 ms
137,536 KB
testcase_41 AC 2,550 ms
138,644 KB
testcase_42 AC 2,576 ms
147,636 KB
testcase_43 AC 2,571 ms
130,000 KB
testcase_44 AC 2,597 ms
144,892 KB
testcase_45 AC 2,564 ms
151,788 KB
testcase_46 AC 2,574 ms
145,564 KB
testcase_47 AC 2,571 ms
137,248 KB
testcase_48 AC 2,602 ms
152,240 KB
testcase_49 AC 2,586 ms
138,832 KB
testcase_50 AC 2,548 ms
136,532 KB
testcase_51 AC 2,561 ms
136,160 KB
testcase_52 AC 2,571 ms
139,588 KB
testcase_53 AC 2,578 ms
137,956 KB
testcase_54 AC 2,604 ms
148,572 KB
testcase_55 AC 2,565 ms
144,544 KB
testcase_56 AC 2,579 ms
150,052 KB
testcase_57 AC 2,582 ms
143,376 KB
testcase_58 AC 2,590 ms
140,292 KB
testcase_59 AC 2,584 ms
131,896 KB
testcase_60 AC 2,560 ms
137,252 KB
testcase_61 AC 2,564 ms
133,444 KB
testcase_62 AC 2,557 ms
134,944 KB
testcase_63 AC 2,582 ms
139,968 KB
testcase_64 AC 2,583 ms
135,092 KB
testcase_65 AC 2,562 ms
140,764 KB
testcase_66 AC 2,566 ms
136,788 KB
testcase_67 AC 2,580 ms
141,292 KB
testcase_68 AC 2,572 ms
136,472 KB
testcase_69 AC 2,578 ms
142,408 KB
testcase_70 AC 2,570 ms
150,376 KB
testcase_71 AC 2,575 ms
149,604 KB
testcase_72 AC 2,574 ms
139,040 KB
testcase_73 AC 2,575 ms
142,724 KB
testcase_74 AC 2,591 ms
141,092 KB
testcase_75 AC 2,546 ms
135,588 KB
testcase_76 AC 2,593 ms
160,000 KB
testcase_77 AC 2,557 ms
134,044 KB
testcase_78 AC 2,596 ms
152,208 KB
testcase_79 AC 2,567 ms
133,636 KB
testcase_80 AC 2,555 ms
139,032 KB
testcase_81 AC 2,554 ms
119,072 KB
testcase_82 AC 2,554 ms
126,928 KB
testcase_83 AC 2,575 ms
131,436 KB
testcase_84 AC 2,581 ms
152,864 KB
testcase_85 AC 2,548 ms
129,528 KB
testcase_86 AC 2,583 ms
148,680 KB
testcase_87 AC 2,570 ms
140,672 KB
testcase_88 AC 2,570 ms
141,276 KB
testcase_89 AC 2,591 ms
143,412 KB
testcase_90 AC 2,547 ms
134,180 KB
testcase_91 AC 2,557 ms
127,080 KB
testcase_92 AC 2,609 ms
163,776 KB
testcase_93 AC 2,582 ms
157,420 KB
testcase_94 AC 2,582 ms
148,684 KB
testcase_95 AC 2,557 ms
138,548 KB
testcase_96 AC 2,562 ms
144,280 KB
testcase_97 AC 2,570 ms
143,800 KB
testcase_98 AC 2,577 ms
147,268 KB
testcase_99 AC 2,579 ms
145,784 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
main.cpp: メンバ関数 ‘std::vector<Pos> Solver::Solve(int)’ 内:
main.cpp:964:3: 警告: 制御が非 void 関数の終りに到達しました [-Wreturn-type]
  964 |   }
      |   ^
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};
  }

  // checkpointを削るDP
  vector<vector<Pos>> DPReduceCheckPoints(
      const vector<Pos>& original_poses) const {
    // dp[i][j] :
    // 現在original_poses[i]にいて、j個のposをskipしているなかで、消費HPの期待値の最小値
    struct Data {
      float hp = INF;
      int prev_i;
      int prev_j;
    };

    int key_idx = 0;
    rep(i, original_poses.size()) {
      if (key_pos == original_poses[i]) {
        key_idx = i;
        break;
      }
    }

    const int max_skip_count = 200;
    vector<vector<Data>> dp(original_poses.size(),
                            vector<Data>(max_skip_count + 1));
    dp[0][0].hp = 0;

    rep(i, (int)original_poses.size() - 1) {
      rep(j, max_skip_count) {
        const auto current_hp = dp[i][j].hp;
        for (int k = 1;
             i + k < (int)original_poses.size() && j + k - 1 <= max_skip_count;
             ++k) {
          const int next_i = i + k;

          if (i < key_idx && key_idx < next_i) {
            break;
          }

          const int next_j = j + k - 1;
          const float next_hp =
              Dist(original_poses[i], original_poses[next_i]) + current_hp;

          auto& next_dp = dp[next_i][next_j];
          if (next_hp < next_dp.hp) {
            next_dp.hp = next_hp;
            next_dp.prev_i = i;
            next_dp.prev_j = j;
          }
        }
      }
    }

    vector<vector<Pos>> ret;
    ret.reserve(max_skip_count + 1);

    rep(skip_count, max_skip_count + 1) {
      int i = (int)original_poses.size() - 1;
      int j = skip_count;
      ret.emplace_back();
      while (i != 0 || j != 0) {
        ret.back().emplace_back(original_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.back().emplace_back(original_poses[i]);
      reverse(all(ret.back()));
    }

    return ret;
  }

  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 Solution {
  vector<Pos> points;
  float hp = INF;
  bitset<3600> exists = 0;

  static Solution CreateGreedy(const Board& board) {
    Solution sol;
    sol.points.emplace_back(board.start_pos);

    set<Pos> S(all(board.check_points));
    S.erase(board.start_pos);
    S.erase(board.goal_pos);

    Pos p = board.start_pos;
    while (S.size() > 0) {
      const auto itr =
          std::min_element(all(S), [&](const auto& l, const auto& r) {
            return board.Dist(p, l) < board.Dist(p, r);
          });
      if (board.Dist(p, *itr) > INF * 0.1) {
        break;
      }

      p = *itr;
      sol.points.emplace_back(p);
      S.erase(itr);
    }

    sol.points.emplace_back(board.goal_pos);

    sol.hp = 0;
    rep(i, (int)sol.points.size()) {
      sol.exists[sol.points[i]] = true;
      if (i + 1 < (int)(sol.points.size())) {
        sol.hp += board.Dist(sol.points[i], sol.points[i + 1]);
      }
    }

    return sol;
  }

  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 NBD {
  float Diff() const { return diff_; }
  virtual void Update(Solution*) const = 0;
  void Restore(Solution*) const {}

  float diff_;
};

struct NBD_swap : public NBD {
  NBD_swap() {}
  NBD_swap(Solution* const sol, const int l, const int r) {
    l_ = l;
    r_ = r;
    diff_ = sol->DiffSwap(global_board, l, r);
  }
  void Update(Solution* sol) const override { sol->Swap(global_board, l_, r_); }

  int l_;
  int r_;
};

struct NBDGenerator {
  NBDGenerator() {}
  NBD* Generate(Solution* const sol, const int g) {
    // swap
    const int r = rand(2, sol->points.size());
    const int l = rand(1, r);
    nbd_swap0 = NBD_swap(sol, l, r);
    return &nbd_swap0;
  }

  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<Solution, NBDGenerator> sa(time_limit - 500, 0.3, 0.1);
    Solution initial_sol = Solution::CreateGreedy(board_);
    auto best_sol = sa.run(initial_sol, nbd_generator);

    const auto skip_count2check_points =
        board_.DPReduceCheckPoints(best_sol.points);

    rep(skip_count, skip_count2check_points.size()) {
      const auto& check_points = skip_count2check_points[skip_count];
      vector<vector<Pos>> keiros;
      rep(j, (int)check_points.size() - 1) {
        auto keiro = board_.Keiro(check_points[j], check_points[j + 1]);
        assert(keiro.size() > 0);
        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)check_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