提出 #63336421


ソースコード 拡げる

#include <bits/stdc++.h>
using namespace std;
#include <atcoder/all>
using namespace atcoder;
// input and output of modint
// istream &operator>>(istream &is, modint998244353 &a) {
//     long long v;
//     is >> v;
//     a = v;
//     return is;
// }
// ostream &operator<<(ostream &os, const modint998244353 &a) {
//     return os << a.val();
// }
// istream &operator>>(istream &is, modint1000000007 &a) {
//     long long v;
//     is >> v;
//     a = v;
//     return is;
// }
// ostream &operator<<(ostream &os, const modint1000000007 &a) {
//     return os << a.val();
// }
// istream &operator>>(istream &is, modint &a) {
//     long long v;
//     is >> v;
//     a = v;
//     return is;
// }
// ostream &operator<<(ostream &os, const modint &a) {
//     return os << a.val();
// }
// template <int m> istream &operator>>(istream &is, static_modint<m> &a) {
//     long long v;
//     is >> v;
//     a = v;
//     return is;
// }
// template <int m> ostream &operator<<(ostream &os, const static_modint<m> &a) {
//     return os << a.val();
// }
#define rep_(i, a_, b_, a, b, ...) for (int i = (a), lim##i = (b); i < lim##i; ++i)
#define rep(i, ...) rep_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)  // rep(i, a): [0, a); rep(i, a, b): [a, b)
#define drep_(i, a_, b_, a, b, ...) for (int i = (a) - 1, lim##i = (b); i >= lim##i; --i)
#define drep(i, ...) drep_(i, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, 0)  // drep(i, a): [0, a); drep(i, a, b): [b, a)
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#ifdef LOCAL
void debug_out() {
    cerr << endl;
}
template <class Head, class... Tail> void debug_out(Head H, Tail... T) {
    cerr << ' ' << H;
    debug_out(T...);
}
#define debug(...) cerr << 'L' << __LINE__ << " [" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#define dump(x) cerr << 'L' << __LINE__ << " " << #x << " = " << (x) << endl
#else
#define debug(...) (void(0))
#define dump(x) (void(0))
#endif
template <class T> using V = vector<T>;
template <class T> using VV = V<V<T>>;
using ll = long long;
using ld = long double;
using Vi = V<int>;
using VVi = VV<int>;
using Vl = V<ll>;
using VVl = VV<ll>;
using Vd = V<ld>;
using VVd = VV<ld>;
using Vb = V<bool>;
using VVb = VV<bool>;
template <class T> using priority_queue_rev = priority_queue<T, vector<T>, greater<T>>;
template <class T> vector<T> make_vec(size_t n, T a) {
    return vector<T>(n, a);
}
template <class... Ts> auto make_vec(size_t n, Ts... ts) {
    return vector<decltype(make_vec(ts...))>(n, make_vec(ts...));
}
template <class T> inline void fin(const T x) {
    cout << x << '\n';
    exit(0);
}
template <class T> inline void deduplicate(vector<T> &a) {
    sort(all(a));
    a.erase(unique(all(a)), a.end());
}
template <class T> inline bool chmin(T &a, const T b) {
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T> inline bool chmax(T &a, const T b) {
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}
template <class T> inline int sz(const T &x) {
    return x.size();
}
template <class T> inline int count_between(const vector<T> &a, T l, T r) {
    return lower_bound(all(a), r) - lower_bound(all(a), l);
}  // [l, r)
template <class T1, class T2> istream &operator>>(istream &is, pair<T1, T2> &p) {
    is >> p.first >> p.second;
    return is;
}
template <class T1, class T2> ostream &operator<<(ostream &os, const pair<T1, T2> &p) {
    os << '(' << p.first << ", " << p.second << ')';
    return os;
}
template <class T, size_t n> istream &operator>>(istream &is, array<T, n> &v) {
    for (auto &e : v) is >> e;
    return is;
}
template <class T, size_t n> ostream &operator<<(ostream &os, const array<T, n> &v) {
    for (auto &e : v) os << e << ' ';
    return os;
}
template <class T> istream &operator>>(istream &is, vector<T> &v) {
    for (auto &e : v) is >> e;
    return is;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &v) {
    for (auto &e : v) os << e << ' ';
    return os;
}
template <class T> istream &operator>>(istream &is, deque<T> &v) {
    for (auto &e : v) is >> e;
    return is;
}
template <class T> ostream &operator<<(ostream &os, const deque<T> &v) {
    for (auto &e : v) os << e << ' ';
    return os;
}
inline ll floor_div(ll x, ll y) {
    if (y < 0) x = -x, y = -y;
    return x >= 0 ? x / y : (x - y + 1) / y;
}  // floor(x/y)
inline ll ceil_div(ll x, ll y) {
    if (y < 0) x = -x, y = -y;
    return x >= 0 ? (x + y - 1) / y : x / y;
}  // ceil(x/y)
inline int floor_log2(const ll x) {
    assert(x > 0);
    return 63 - __builtin_clzll(x);
}  // floor(log2(x))
inline int ceil_log2(const ll x) {
    assert(x > 0);
    return (x == 1) ? 0 : 64 - __builtin_clzll(x - 1);
}  // ceil(log2(x))
inline int popcount(const ll x) {
    return __builtin_popcountll(x);
}
struct fast_ios {
    fast_ios() {
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(20);
    };
} fast_ios_;
constexpr int INF = numeric_limits<int>::max() >> 1;
// constexpr ll INFll = numeric_limits<ll>::max() >> 1;
// constexpr ld EPS = 1e-10;
// const ld PI = acos(-1.0);
// using mint = modint998244353;
// using mint = modint1000000007;
// using mint = modint;
// using Vm = V<mint>; using VVm = VV<mint>;

// 時間計測
auto system_now = std::chrono::system_clock::now();
int check_time() {
    auto now = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(now - system_now).count();
}

#if DEBUG
constexpr float machine_spec = 0.5;
#elif LOCAL
constexpr float machine_spec = 1.4;
#else
constexpr float machine_spec = 1;
#endif
constexpr ll time_limit = (3000-50);

struct Timer{
    private:
        chrono::system_clock::time_point start = chrono::system_clock::now();
        chrono::system_clock::time_point lap = chrono::system_clock::now();
    public:
        // 実行開始時からの経過時間(ms)を返す
        int elapsed_ms(){
            auto now = chrono::system_clock::now();
            #if DEBUG or LOCAL
                return std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count() * machine_spec;
            #else
                return std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
            #endif
        }
        // ラップタイム(ms)を返す
        int lap_ms(){
            auto now = chrono::system_clock::now();
            #if DEBUG or LOCAL
                return std::chrono::duration_cast<std::chrono::milliseconds>(now - lap).count() * machine_spec;
            #else
                return std::chrono::duration_cast<std::chrono::milliseconds>(now - lap).count();
            #endif
        }
        // ラップを刻む
        void mark_lap(){
            lap = chrono::system_clock::now();
        }
} timer;

// 乱数
struct Xorshift {
    uint32_t x = 123456789, y = 362436069, z = 521288629, w = 88675123;

    uint32_t rand_int() {
        uint32_t t = x ^ (x << 11);
        x = y;
        y = z;
        z = w;
        return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
    }

    // 0以上mod未満の整数を乱択
    uint32_t rand_int(uint32_t mod) {
        return rand_int() % mod;
    }

    // l以上r未満の整数を乱択
    uint32_t rand_int(uint32_t l, uint32_t r) {
        assert(l < r);
        return l + rand_int(r - l);
    }

    // 0以上1以下の実数を乱沢
    double rand_double() {
        return (double)rand_int() / UINT32_MAX;
    }
};
Xorshift xor_shift;
int rnd() {
    return xor_shift.rand_int();
}

// 向き
const vector<int> Dx = {0, -1, 0, 1, 0};
const vector<int> Dy = {-1, 0, 1, 0, 0};
const vector<char> Dc = {'L', 'U', 'R', 'D', '.'};

// グローバル変数
constexpr int N = 20;
constexpr int N2 = N * N;
constexpr int MaxT = 2000;
int M;
char B[N][N];
int sx, sy;
int problem_type;


vector<string> C(N);

namespace opt {
using ai2 = array<int, 2>;
using ai400 = array<array<int, N>, N>;
ai2 start_pos;

constexpr array<array<int, 2>, 4> dir{{
    {1, 0},
    {0, 1},
    {-1, 0},
    {0, -1},
}};

// スタート地点から S の方向の岩を全て処理するとした場合の,穴から各マスへの距離
pair<int, vector<vector<int>>> compute_dist(int S) {
  int c = 0;
  auto [si, sj] = start_pos;

  auto dist = make_vec(N, N, INF);
  queue<ai2> que;
  dist[si][sj] = 0;
  que.push({si, sj});

  rep(k, 4) {
    auto [di, dj] = dir[k];
    auto [i, j] = start_pos;
    int d_max = 0;
    int d = 0;
    while (true) {
      i += di;
      j += dj;
      d++;
      if (i < 0 || i >= N || j < 0 || j >= N) break;
      if (C[i][j] == '@') {
        if (!(S >> k & 1))
          break;
        else {
          chmax(d_max, d);
          c++;
        }
      }
      dist[i][j] = 0;
      que.push({i, j});
    }
    c += d;
  }

  while (!que.empty()) {
    auto [i, j] = que.front();
    que.pop();
    for (auto [di, dj] : dir) {
      int i2 = i + di;
      int j2 = j + dj;
      if (i2 < 0 || i2 >= N || j2 < 0 || j2 >= N) continue;
      // if (C[i2][j2] == '@') continue;
      if (chmin(dist[i2][j2], dist[i][j] + 1)) {
        que.push({i2, j2});
      }
    }
  }
  return {c, dist};
}

struct Edge {
  int u, v, w;
};

pair<int, vector<ai2>> compute_cost_rocks(vector<vector<int>> cost) {
  // 岩でないマスは cost を 0 に上書き
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      if (C[i][j] != '@') cost[i][j] = 0;
    }
  }

  // 岩でないマスのみを対象として連結成分を求める
  vector<vector<int>> compId(N, vector<int>(N, -1));
  vector<vector<ai2>> comps;  // comps[k] : 成分 k に属するマスのリスト
  int compCount = 0;
  int di[4] = {1, 0, -1, 0}, dj[4] = {0, 1, 0, -1};
  // for (int i = 0; i < N; i++) {
  //   for (int j = 0; j < N; j++) {
  //     if (C[i][j] == '@' || compId[i][j] != -1) continue;
  //     comps.push_back({});
  //     queue<ai2> q;
  //     q.push({i, j});
  //     compId[i][j] = compCount;
  //     while (!q.empty()) {
  //       ai2 cur = q.front();
  //       q.pop();
  //       comps[compCount].push_back(cur);
  //       int x = cur[0], y = cur[1];
  //       for (int k = 0; k < 4; k++) {
  //         int nx = x + di[k], ny = y + dj[k];
  //         if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
  //         if (C[nx][ny] == '@' || compId[nx][ny] != -1) continue;
  //         compId[nx][ny] = compCount;
  //         q.push({nx, ny});
  //       }
  //     }
  //     compCount++;
  //   }
  // }
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      if (C[i][j] == '@' || compId[i][j] != -1) continue;
      vector<ai2> curComp;  // 現在の成分のセルを一時格納
      queue<ai2> q;
      q.push({i, j});
      compId[i][j] = -2;  // 一時的なマーカー
      while (!q.empty()) {
        ai2 cur = q.front();
        q.pop();
        curComp.push_back(cur);
        int x = cur[0], y = cur[1];
        for (int k = 0; k < 4; k++) {
          int nx = x + di[k], ny = y + dj[k];
          if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
          if (C[nx][ny] == '@' || compId[nx][ny] != -1) continue;
          compId[nx][ny] = -2;
          q.push({nx, ny});
        }
      }
      // 鉱石(小文字)または穴(大文字)が含まれているかチェック
      bool valid = false;
      for (auto &cell : curComp) {
        char c = C[cell[0]][cell[1]];
        if (isalpha(c)) {
          valid = true;
          break;
        }
      }
      if (valid) {
        for (auto &cell : curComp) {
          compId[cell[0]][cell[1]] = compCount;
        }
        comps.push_back(curComp);
        compCount++;
      } else {
        // 無効な成分は compId を元に戻す
        for (auto &cell : curComp) {
          compId[cell[0]][cell[1]] = -1;
        }
      }
    }
  }

  // 結果格納用
  // distBetween[i][j] : 連結成分 i から成分 j への最短通過コスト
  // pathRocks[i][j]   : その最短経路上の岩('@')のマスのリスト
  vector<vector<int>> distBetween(compCount, vector<int>(compCount, INF));
  vector<vector<vector<ai2>>> pathRocks(compCount, vector<vector<ai2>>(compCount));

  // 各成分を始点として多始点Dijkstra を実施
  for (int comp = 0; comp < compCount; comp++) {
    vector<vector<int>> dist(N, vector<int>(N, INF));
    // 経路復元用:各マスの直前の座標。始点は {-1, -1} とする
    vector<vector<ai2>> prev(N, vector<ai2>(N, {-1, -1}));
    // 優先度付きキュー:(累積コスト, x, y)
    priority_queue<tuple<int, int, int>, vector<tuple<int, int, int>>, greater<>> pq;
    // 成分 comp の各マスを出発点として登録
    for (auto &cell : comps[comp]) {
      int x = cell[0], y = cell[1];
      dist[x][y] = 0;
      prev[x][y] = {-1, -1};
      pq.push({0, x, y});
    }
    // Dijkstra の実施
    while (!pq.empty()) {
      auto [d, x, y] = pq.top();
      pq.pop();
      if (d > dist[x][y]) continue;
      for (int k = 0; k < 4; k++) {
        int nx = x + di[k], ny = y + dj[k];
        if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
        int nd = d + cost[nx][ny];
        if (nd < dist[nx][ny]) {
          dist[nx][ny] = nd;
          prev[nx][ny] = {x, y};
          pq.push({nd, nx, ny});
        }
      }
    }
    // 各ターゲット成分 j に対して,成分 j 内のマスの中で最短距離となるものを選ぶ
    for (int j = 0; j < compCount; j++) {
      int best = INF;
      ai2 bestCell = {-1, -1};
      for (auto &cell : comps[j]) {
        int x = cell[0], y = cell[1];
        if (dist[x][y] < best) {
          best = dist[x][y];
          bestCell = {x, y};
        }
      }
      distBetween[comp][j] = best;
      if (best == INF) continue;  // 経路が存在しない場合はスキップ

      // bestCell から出発点までの経路を復元
      vector<ai2> path;
      for (ai2 cur = bestCell; cur[0] != -1; cur = prev[cur[0]][cur[1]]) path.push_back(cur);
      reverse(path.begin(), path.end());

      // 経路上の岩('@')のマスのみ抽出
      vector<ai2> rocks;
      for (auto &cell : path) {
        int x = cell[0], y = cell[1];
        if (C[x][y] == '@') rocks.push_back(cell);
      }
      pathRocks[comp][j] = rocks;
    }
  }

  // MST のための辺リスト作成 (距離行列は対称なので distBetween[i][j] をそのまま使用)
  vector<Edge> edges;
  for (int i = 0; i < compCount; i++) {
    for (int j = i + 1; j < compCount; j++) {
      if (distBetween[i][j] < INF) edges.push_back({i, j, distBetween[i][j]});
    }
  }
  sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b) { return a.w < b.w; });

  dsu uf(compCount);
  int totalCost = 0;
  vector<Edge> mstEdges;
  for (auto &e : edges) {
    if (!uf.same(e.u, e.v)) {
      uf.merge(e.u, e.v);
      totalCost += e.w;
      mstEdges.push_back(e);
    }
  }

  vector<ai2> rocks;
  for (auto &e : mstEdges) {
    rocks.insert(rocks.end(), all(pathRocks[e.u][e.v]));
  }
  return pair{totalCost, rocks};
}

vector<ai2> precompute(vector<string> C) {
  rep(i, N) rep(j, N) {
    if (C[i][j] == 'A') {
      start_pos = {i, j};
      break;
    }
  }

  int min_cost = INF;
  vector<ai2> rocks_to_remove;

  // 穴から各 4 方向の岩を全除去するか否か,2^4 通り全探索
  rep(S, 16) {
    // 各岩を除去するコストを計算
    auto [c, cost_mat] = compute_dist(S);
    // 除去すべき岩を計算
    auto [c2, rocks] = compute_cost_rocks(cost_mat);

    // 下の係数の 2 はマジックナンバー.調整すべきかも
    int c3 = c + 2 * c2;

    if (chmin(min_cost, c3)) {
      swap(rocks_to_remove, rocks);
      rep(k, 4) if (S >> k & 1) {
        auto [di, dj] = dir[k];
        auto [i, j] = start_pos;
        while (true) {
          i += di;
          j += dj;
          if (i < 0 || i >= N || j < 0 || j >= N) break;
          if (C[i][j] == '@') rocks_to_remove.push_back({i, j});
        }
      }
    }
  }
  return rocks_to_remove;
}
}


void input() {
    int _; cin >> _;
    cin >> M;
    rep(i,0,N)rep(j,0,N) cin >> B[i][j];
    rep(i,0,N) rep(j,0,N) if(B[i][j] == 'A') sx = i, sy = j;
    // BをCに入れる
    rep(i,0,N) C[i].resize(N);
    rep(i,0,N)rep(j,0,N) C[i][j] = B[i][j];
}

string DIRS = "LURD";
int dx[] = {0, -1, 0, 1};
int dy[] = {-1, 0, 1, 0};

bool is_valid(int x, int y){
    return 0 <= x && x < N && 0 <= y && y < N;
}

bool is_ore(char c){
    return c >= 'a' && c <= 'c';
}

// 各マスの各鉱石種類について距離
struct DistanceMap{
    int dist[3][N][N];
    void init(char B[N][N]){
        rep(i,0,N)rep(j,0,N) rep(k,0,3) dist[k][i][j] = INF;
        rep(i,0,3){
            // 'a'+iの鉱石までの距離をBFSで計算
            int gx = -1, gy = -1;
            rep(x,0,N)rep(y,0,N) if(B[x][y] == 'A'+i) gx = x, gy = y;
            if(gx == -1) continue;
            rep(x,0,N)rep(y,0,N) dist[i][x][y] = abs(x-gx) + abs(y-gy);
        }

        // C問題用
        int rock_cnt = 0; rep(i,0,N)rep(j,0,N) rock_cnt += B[i][j] == '@';
        if(rock_cnt >= N2 / 3){
            rep(i,0,N)rep(j,0,N) dist[0][i][j] = 999;
            // bfsで距離を埋める。Aだけ
            VV<bool> visited(N, V<bool>(N, false));
            queue<tuple<int, int, int>> q;
            q.push({sx, sy, 0});
            while(!q.empty()){
                auto [x, y, d] = q.front(); q.pop();
                if(visited[x][y]) continue;
                visited[x][y] = true;
                dist[0][x][y] = d;
                rep(k,0,4){
                    int nx = x + dx[k];
                    int ny = y + dy[k];
                    if(!is_valid(nx, ny)) continue;
                    if(B[nx][ny] == '@') continue;
                    q.push({nx, ny, d+1});
                }
            }
        }
        // cerr distmap
        rep(i,0,N){
            rep(j,0,N){
                cerr << setw(3) << dist[0][i][j] << " ";
            }
            cerr << endl;
        }
    }
};

// 盤面に適当なHashを割り振る
struct BoardHash{
    ll hash[4][N][N];
    BoardHash(){
        // llの乱数生成気
        mt19937 mt(0);
        rep(i,0,4)rep(x,0,N)rep(y,0,N) hash[i][x][y] = mt();
    }
} board_hash;

struct OutputDebugger{
    void check(V<string> ans){
        // return ;
        char b[N][N];
        memcpy(b, B, sizeof(B));
        int cx = sx, cy = sy;
        rep(i,0,sz(ans)){
            string s = ans[i];
            // sをsplit
            int op = s[0] - '0';
            if(op == 1){
                char dir = s[2];
                if(dir == 'L') cy--;
                if(dir == 'U') cx--;
                if(dir == 'R') cy++;
                if(dir == 'D') cx++;
                assert(is_valid(cx, cy));
            }else if(op == 2){
                char dir_char = s[2];
                int dir = DIRS.find(dir_char);
                // move object
                int nx = cx + dx[dir];
                int ny = cy + dy[dir];
                assert(is_valid(nx, ny));
                char c = b[cx][cy];
                bool is_goal = (b[nx][ny] == 'A' + c - 'a');
                assert(b[nx][ny] == '.' || is_goal);
                b[cx][cy] = '.';
                if(!is_goal) b[nx][ny] = c;
                cx = nx, cy = ny;
            }else if(op == 3){
                char dir_char = s[2];
                int dir = DIRS.find(dir_char);
                // roll object
                int nx = cx, ny = cy;
                while(true){
                    int nnx = nx + dx[dir];
                    int nny = ny + dy[dir];
                    if(!is_valid(nnx, nny)){
                        break;
                    }
                    if(b[nnx][nny] == '@' || b[nnx][nny] >= 'a') break;
                    nx = nnx;
                    ny = nny;
                    if(b[nnx][nny] == 'A' || b[nnx][nny] == 'B' || b[nnx][nny] == 'C'){
                        nx = -1, ny = -1;
                        break;
                    }
                }
                if(nx >= 0){
                    assert(b[nx][ny] == '.');
                    char c = b[cx][cy];
                    b[cx][cy] = '.';
                    b[nx][ny] = c;
                }
                else{
                    b[cx][cy] = '.';
                }
            }
        }
    }
} output_debugger;

struct Operation{
    int x, y; // どの座標のオブジェクトを移動させるか
    int dir; // 0:L, 1:U, 2:R, 3:D
    bool is_roll; // 0:ひとマス移動, 1:転がす
};

struct State{
    char b[N][N];
    int cx, cy;
    Operation cur_op; // 今回の操作
    pair<int, int> prev_state; // 1つ前の状態. (ターン数, ビームの何番目に入れてるか)
    int cost = 0; // 全鉱石の穴までの距離の合計
    ll hash = 0;

    void debug_log_board(){
        rep(i,0,N){
            rep(j,0,N){
                cerr << b[i][j];
            }
            cerr << endl;
        }
    }

    bool operator<(const State &s) const {
        return cost < s.cost;
    }
};

struct BeamSearch{
    DistanceMap dmap;
    bool is_move_rock = true; // 岩もビムサで動かすか。C問題ではfalse想定. rockは'@'

    void output(int turn, State& state, V<State> beams[MaxT]){
        V<Operation> ops;
        V<State> states;
        while(true){
            ops.push_back(state.cur_op);
            states.push_back(state);
            if(state.prev_state.first == 0) break;
            state = beams[state.prev_state.first][state.prev_state.second];
        }
        reverse(all(ops));
        reverse(all(states));
        // for(auto state: states){
        //     state.debug_log_board();
        //     cerr << endl;
        // }

        V<string> ans;
        int cx = sx, cy = sy;
        for(auto& op: ops){
            // (op.x, op.y)に移動
            while(cx != op.x){
                cout << "1 " << (cx < op.x ? 'D' : 'U') << endl;
                ans.push_back("1 " + string(1, cx < op.x ? 'D' : 'U'));
                cx += (cx < op.x ? 1 : -1);
            }
            while(cy != op.y){
                cout << "1 " << (cy < op.y ? 'R' : 'L') << endl;
                ans.push_back("1 " + string(1, cy < op.y ? 'R' : 'L'));
                cy += (cy < op.y ? 1 : -1);
            }
            if(!op.is_roll){
                cout << "2 " << DIRS[op.dir] << endl;
                ans.push_back("2 " + string(1, DIRS[op.dir]));
                // charaも移動
                cx += dx[op.dir];
                cy += dy[op.dir];
            }else{
                cout << "3 " << DIRS[op.dir] << endl;
                ans.push_back("3 " + string(1, DIRS[op.dir]));
            }
        }
        cerr << "Score = " << turn << endl;
        output_debugger.check(ans);
    }

    State create_init_state(){
        State state;
        memcpy(state.b, B, sizeof(B));
        rep(i,0,N)rep(j,0,N) if(state.b[i][j] == 'A') state.cx = i, state.cy = j;
        
        // cost計算
        rep(i,0,N)rep(j,0,N) if(is_ore(state.b[i][j])){
            int ore_type = state.b[i][j] - 'a';
            state.cost += dmap.dist[ore_type][i][j];
            state.hash ^= board_hash.hash[ore_type][i][j];
        }
        rep(i,0,N)rep(j,0,N) if(state.b[i][j] == '@'){
            state.hash ^= board_hash.hash[3][i][j];
        }
        return state;
    }

    void run(){
        dmap.init(B);

        State init_state = create_init_state();
        is_move_rock = problem_type == 3;
        unordered_set<ll> hashes;
        static priority_queue<State> beams[MaxT];
        static V<State> caches[MaxT];

        State best_state = init_state;
        int best_cost = init_state.cost;
        int best_turn = 0;
        
        int beam_width = 80;
        beams[0].push(init_state);
        hashes.insert(init_state.hash);

        rep(t,0,MaxT){
            if(t%10 == 0) cerr << "t: " << t << " beams: " << beams[t].size() << endl;
            if(t > 1000){
                cerr << "t: " << t << " beams: " << beams[t].size() << endl;

            }
            if(beams[t].empty()) continue;
            while(!beams[t].empty()){
                caches[t].push_back(beams[t].top());
                beams[t].pop();
            }
            reverse(all(caches[t]));
            if(caches[t][0].cost == 0){
                // 全て穴に落ちたので終了
                output(t, caches[t][0], caches);
                return;
            }
            if(chmin(best_cost, caches[t][0].cost)){
                best_state = caches[t][0];
                best_turn = t;
            }

            // 次のビームを生成
            rep(si,0,min(beam_width, sz(caches[t]))){
                auto& state = caches[t][si];
                // 全ての鉱石について、8通りの操作を試す(is_move_rockなら、岩も操作対象)
                rep(i,0,N)rep(j,0,N){
                    char c = state.b[i][j];
                    int ore_type = c - 'a';
                    char goal_char = 'A' + ore_type;
                    bool is_rock = c == '@';
                    if(is_move_rock){
                        if(!is_rock && c < 'D') continue;
                    }else{
                        if(c < 'D') continue;
                    }
                    
                    int chara_move_turn = abs(i-state.cx) + abs(j-state.cy);
                    int next_turn = t + chara_move_turn + 1; // Objectを動かすのにかかるコスト=1
                    if(next_turn >= MaxT) continue;
                    rep(dir,0,4){
                        int nx = i + dx[dir];
                        int ny = j + dy[dir];
                        // 盤面外に出るならスキップ
                        // TODO: 岩は落としても良いので要対応。ただ、一旦この操作を無しにしても、さほど影響がないため、実装を楽にするためまずは無しで。
                        if(!is_valid(nx, ny)) continue;
                        // 1マスも動けない場合はスキップ
                        if(state.b[nx][ny] == '@' || state.b[nx][ny] >= 'a') continue; 
                        // 異なるゴールに落ちる場合もスキップ
                        if(state.b[nx][ny] != '.' && state.b[nx][ny] != goal_char) continue;

                        rep(op_type,0,2){ // 0:ひとマス移動, 1:転がす
                            // next_stateを生成する前に、実際に操作を行った後の状態をシミュレートして、コストを計算。棄却されるなら、next_stateを生成しない。
                            int next_cost;
                            // オブジェクトがどこに移動するか。転がす場合だけ更新する。
                            int mx = nx, my = ny;
                            if(op_type == 0){
                                // 1マス動かす
                                next_cost = is_rock ? state.cost : state.cost - dmap.dist[ore_type][i][j] + dmap.dist[ore_type][nx][ny];
                                if(beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
                            }else{
                                // 転がす
                                // 鉱石が異なる穴or盤面外に落ちたらNG
                                if(state.b[nx][ny] == goal_char) continue;
                                bool is_ok = true;
                                while(true){
                                    int nnx = mx + dx[dir];
                                    int nny = my + dy[dir];
                                    if(!is_valid(nnx, nny)){
                                        break;
                                    }
                                    if(state.b[nnx][nny] == '@' || state.b[nnx][nny] >= 'a') break;
                                    mx = nnx;
                                    my = nny;
                                    if(state.b[nnx][nny] == goal_char) break;
                                    if(state.b[nnx][nny] != '.' && state.b[nnx][nny] != goal_char){
                                        mx = -1, my = -1;
                                        is_ok = false;
                                        break;
                                    }
                                }
                                if(!is_ok) continue;
                                next_cost = is_rock ? state.cost : state.cost - dmap.dist[ore_type][i][j] + dmap.dist[ore_type][mx][my];
                                if(beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
                            }

                            ll next_hash = state.hash;
                            int hash_type = is_rock ? 3 : ore_type;
                            next_hash ^= board_hash.hash[hash_type][i][j];
                            if(mx >= 0) next_hash ^= board_hash.hash[hash_type][mx][my];
                            if(hashes.count(next_hash)) continue;
                            hashes.insert(next_hash);

                            State next_state = state;
                            next_state.hash = next_hash;
                            next_state.cur_op = {i, j, dir, op_type == 1};
                            next_state.prev_state = {t, si};
                            next_state.cost = next_cost;
                            next_state.b[i][j] = '.';
                            // ひとマス移動の場合は自分も動く
                            next_state.cx = op_type == 0 ? nx : i;
                            next_state.cy = op_type == 0 ? ny : j;
                            // NOTE: 盤面外に出たことを(-1,-1)で表現してあるので、mx >= 0 だけで判定してる
                            if(mx >= 0 && next_state.b[mx][my] != goal_char){
                                next_state.b[mx][my] = c;
                            }
                            beams[t+chara_move_turn+1].push(next_state);
                            if(beams[t+chara_move_turn+1].size() > beam_width)
                                beams[t+chara_move_turn+1].pop();
                        }
                    }
                }
            }
        }

        // クリア出来なかった
        cerr << "Failed to clear" << endl;
        output(MaxT, best_state, caches);
        assert(0);
    }
};

struct Solver{
    void check_probrem_type(){
        int rock_cnt = 0;
        rep(i,0,N)rep(j,0,N) rock_cnt += B[i][j] == '@';
        set<int> ore_set;
        rep(i,0,N)rep(j,0,N) if(is_ore(B[i][j])) ore_set.insert(B[i][j]);
        int ore_cnt = sz(ore_set);
        problem_type = 1;
        if(ore_cnt == 3) problem_type = 2;
        if(rock_cnt >= N2 / 3) problem_type = 3;
    }

    void run(){
        auto replaces = opt::precompute(C);
        for(auto [x, y]: replaces){
            B[x][y] = 'a';
        }
        BeamSearch beam_search;
        beam_search.run();
    }
};

int main() {
#if DEBUG
    freopen("tools/inC/0011.txt", "r", stdin);
#endif
    input();

    Solver solver;
    solver.run();
}

提出情報

提出日時
問題 C - Ore Rolling (C)
ユーザ opt
言語 C++ 20 (gcc 12.2)
得点 828042450
コード長 32242 Byte
結果 AC
実行時間 1630 ms
メモリ 127484 KiB

コンパイルエラー

Main.cpp: In member function ‘void BeamSearch::run()’:
Main.cpp:859:60: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
  859 |                                 if(beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
Main.cpp:883:60: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
  883 |                                 if(beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
Main.cpp:907:66: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
  907 |                             if(beams[t+chara_move_turn+1].size() > beam_width)
      |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
Main.cpp:794:13: warning: variable ‘best_turn’ set but not used [-Wunused-but-set-variable]
  794 |         int best_turn = 0;
      |             ^~~~~~~~~

ジャッジ結果

セット名 test_ALL
得点 / 配点 828042450 / 1500000000
結果
AC × 150
セット名 テストケース
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
ケース名 結果 実行時間 メモリ
test_0000.txt AC 929 ms 60788 KiB
test_0001.txt AC 1315 ms 79944 KiB
test_0002.txt AC 651 ms 45524 KiB
test_0003.txt AC 1047 ms 63208 KiB
test_0004.txt AC 1028 ms 61276 KiB
test_0005.txt AC 784 ms 52748 KiB
test_0006.txt AC 1182 ms 71768 KiB
test_0007.txt AC 954 ms 64852 KiB
test_0008.txt AC 713 ms 45520 KiB
test_0009.txt AC 780 ms 53472 KiB
test_0010.txt AC 686 ms 45276 KiB
test_0011.txt AC 991 ms 65944 KiB
test_0012.txt AC 1630 ms 127484 KiB
test_0013.txt AC 866 ms 55172 KiB
test_0014.txt AC 769 ms 53296 KiB
test_0015.txt AC 884 ms 54356 KiB
test_0016.txt AC 888 ms 60688 KiB
test_0017.txt AC 909 ms 55272 KiB
test_0018.txt AC 1012 ms 60544 KiB
test_0019.txt AC 1006 ms 62848 KiB
test_0020.txt AC 703 ms 45952 KiB
test_0021.txt AC 540 ms 41680 KiB
test_0022.txt AC 800 ms 53696 KiB
test_0023.txt AC 907 ms 56548 KiB
test_0024.txt AC 799 ms 50772 KiB
test_0025.txt AC 965 ms 59392 KiB
test_0026.txt AC 866 ms 56740 KiB
test_0027.txt AC 1064 ms 62296 KiB
test_0028.txt AC 940 ms 55908 KiB
test_0029.txt AC 1122 ms 64176 KiB
test_0030.txt AC 629 ms 42044 KiB
test_0031.txt AC 1124 ms 66760 KiB
test_0032.txt AC 1120 ms 65356 KiB
test_0033.txt AC 981 ms 63168 KiB
test_0034.txt AC 1197 ms 70832 KiB
test_0035.txt AC 648 ms 46744 KiB
test_0036.txt AC 1053 ms 63200 KiB
test_0037.txt AC 869 ms 55032 KiB
test_0038.txt AC 477 ms 35348 KiB
test_0039.txt AC 828 ms 54492 KiB
test_0040.txt AC 535 ms 41248 KiB
test_0041.txt AC 1029 ms 69732 KiB
test_0042.txt AC 913 ms 57784 KiB
test_0043.txt AC 1034 ms 67616 KiB
test_0044.txt AC 938 ms 57020 KiB
test_0045.txt AC 1002 ms 73460 KiB
test_0046.txt AC 1449 ms 89688 KiB
test_0047.txt AC 578 ms 40512 KiB
test_0048.txt AC 508 ms 39500 KiB
test_0049.txt AC 732 ms 53288 KiB
test_0050.txt AC 1093 ms 65524 KiB
test_0051.txt AC 988 ms 61764 KiB
test_0052.txt AC 1207 ms 76400 KiB
test_0053.txt AC 698 ms 45908 KiB
test_0054.txt AC 731 ms 55508 KiB
test_0055.txt AC 936 ms 63752 KiB
test_0056.txt AC 972 ms 65580 KiB
test_0057.txt AC 768 ms 51728 KiB
test_0058.txt AC 628 ms 44716 KiB
test_0059.txt AC 801 ms 56496 KiB
test_0060.txt AC 706 ms 43656 KiB
test_0061.txt AC 976 ms 57272 KiB
test_0062.txt AC 1086 ms 69236 KiB
test_0063.txt AC 446 ms 33228 KiB
test_0064.txt AC 696 ms 46512 KiB
test_0065.txt AC 630 ms 44692 KiB
test_0066.txt AC 905 ms 55056 KiB
test_0067.txt AC 656 ms 44664 KiB
test_0068.txt AC 917 ms 55328 KiB
test_0069.txt AC 881 ms 58436 KiB
test_0070.txt AC 915 ms 61916 KiB
test_0071.txt AC 715 ms 46636 KiB
test_0072.txt AC 802 ms 51296 KiB
test_0073.txt AC 782 ms 51680 KiB
test_0074.txt AC 829 ms 55792 KiB
test_0075.txt AC 1203 ms 66768 KiB
test_0076.txt AC 916 ms 59232 KiB
test_0077.txt AC 889 ms 53732 KiB
test_0078.txt AC 1140 ms 66956 KiB
test_0079.txt AC 802 ms 51316 KiB
test_0080.txt AC 826 ms 54456 KiB
test_0081.txt AC 877 ms 51048 KiB
test_0082.txt AC 854 ms 54148 KiB
test_0083.txt AC 827 ms 53900 KiB
test_0084.txt AC 953 ms 59484 KiB
test_0085.txt AC 1100 ms 68336 KiB
test_0086.txt AC 1097 ms 65452 KiB
test_0087.txt AC 1331 ms 81152 KiB
test_0088.txt AC 887 ms 57328 KiB
test_0089.txt AC 995 ms 62552 KiB
test_0090.txt AC 910 ms 56004 KiB
test_0091.txt AC 994 ms 70836 KiB
test_0092.txt AC 730 ms 49044 KiB
test_0093.txt AC 729 ms 50096 KiB
test_0094.txt AC 612 ms 44940 KiB
test_0095.txt AC 529 ms 39020 KiB
test_0096.txt AC 730 ms 52752 KiB
test_0097.txt AC 545 ms 40700 KiB
test_0098.txt AC 741 ms 51948 KiB
test_0099.txt AC 986 ms 67840 KiB
test_0100.txt AC 811 ms 52268 KiB
test_0101.txt AC 659 ms 46908 KiB
test_0102.txt AC 891 ms 59876 KiB
test_0103.txt AC 740 ms 51524 KiB
test_0104.txt AC 1007 ms 69116 KiB
test_0105.txt AC 1249 ms 78012 KiB
test_0106.txt AC 1270 ms 82100 KiB
test_0107.txt AC 673 ms 46084 KiB
test_0108.txt AC 739 ms 53636 KiB
test_0109.txt AC 693 ms 47416 KiB
test_0110.txt AC 1105 ms 66788 KiB
test_0111.txt AC 925 ms 71100 KiB
test_0112.txt AC 874 ms 56800 KiB
test_0113.txt AC 1204 ms 72244 KiB
test_0114.txt AC 861 ms 55648 KiB
test_0115.txt AC 963 ms 61372 KiB
test_0116.txt AC 1025 ms 67472 KiB
test_0117.txt AC 889 ms 60952 KiB
test_0118.txt AC 1242 ms 72848 KiB
test_0119.txt AC 722 ms 48376 KiB
test_0120.txt AC 827 ms 54816 KiB
test_0121.txt AC 627 ms 42456 KiB
test_0122.txt AC 827 ms 54908 KiB
test_0123.txt AC 649 ms 43748 KiB
test_0124.txt AC 920 ms 58092 KiB
test_0125.txt AC 669 ms 46584 KiB
test_0126.txt AC 516 ms 36700 KiB
test_0127.txt AC 614 ms 39892 KiB
test_0128.txt AC 1356 ms 79404 KiB
test_0129.txt AC 661 ms 42024 KiB
test_0130.txt AC 538 ms 38712 KiB
test_0131.txt AC 1014 ms 68872 KiB
test_0132.txt AC 1101 ms 66700 KiB
test_0133.txt AC 705 ms 46760 KiB
test_0134.txt AC 832 ms 50184 KiB
test_0135.txt AC 577 ms 38200 KiB
test_0136.txt AC 1081 ms 60332 KiB
test_0137.txt AC 967 ms 61340 KiB
test_0138.txt AC 781 ms 51912 KiB
test_0139.txt AC 1001 ms 60156 KiB
test_0140.txt AC 1238 ms 66196 KiB
test_0141.txt AC 729 ms 46040 KiB
test_0142.txt AC 824 ms 57140 KiB
test_0143.txt AC 823 ms 55988 KiB
test_0144.txt AC 612 ms 42920 KiB
test_0145.txt AC 537 ms 38468 KiB
test_0146.txt AC 662 ms 41976 KiB
test_0147.txt AC 851 ms 59652 KiB
test_0148.txt AC 779 ms 51328 KiB
test_0149.txt AC 824 ms 54036 KiB