提出 #63337972
ソースコード 拡げる
#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;
}
} // namespace opt
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] = round(100 * pow(abs(x - gx) + abs(y - gy), 0.6));
}
// 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;
int rock_dist = 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 {
if (cost != s.cost)
return cost < s.cost;
else
return rock_dist > s.rock_dist;
}
};
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];
state.rock_dist += dmap.dist[0][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 = 60;
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 next_rock_dist = state.rock_dist;
// オブジェクトがどこに移動するか。転がす場合だけ更新する。
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 (is_rock) next_rock_dist = state.rock_dist - dmap.dist[0][i][j] + dmap.dist[0][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 (is_rock) next_rock_dist = state.rock_dist - dmap.dist[0][i][j] + dmap.dist[0][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.rock_dist = next_rock_dist;
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);
}
};
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();
}
提出情報
提出日時
2025-03-02 18:59:29+0900
問題
B - Ore Rolling (B)
ユーザ
opt
言語
C++ 23 (gcc 12.2)
得点
784189510
コード長
34135 Byte
結果
AC
実行時間
1551 ms
メモリ
68280 KiB
コンパイルエラー
Main.cpp: In member function ‘void BeamSearch::run()’:
Main.cpp:866:61: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
866 | if (beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
Main.cpp:891:61: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
891 | if (beams[next_turn].size() >= beam_width && next_cost >= beams[next_turn].top().cost) continue;
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
Main.cpp:916:71: warning: comparison of integer expressions of different signedness: ‘std::priority_queue<State>::size_type’ {aka ‘long unsigned int’} and ‘int’ [-Wsign-compare]
916 | if (beams[t + chara_move_turn + 1].size() > beam_width)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
Main.cpp:800:13: warning: variable ‘best_turn’ set but not used [-Wunused-but-set-variable]
800 | int best_turn = 0;
| ^~~~~~~~~
ジャッジ結果
セット名
test_ALL
得点 / 配点
784189510 / 1500000000
結果
セット名
テストケース
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
1333 ms
62272 KiB
test_0001.txt
AC
1335 ms
59156 KiB
test_0002.txt
AC
1216 ms
58588 KiB
test_0003.txt
AC
1327 ms
60760 KiB
test_0004.txt
AC
1253 ms
56628 KiB
test_0005.txt
AC
1287 ms
61604 KiB
test_0006.txt
AC
1271 ms
60236 KiB
test_0007.txt
AC
1025 ms
50824 KiB
test_0008.txt
AC
1199 ms
55236 KiB
test_0009.txt
AC
1109 ms
53516 KiB
test_0010.txt
AC
1152 ms
56396 KiB
test_0011.txt
AC
1202 ms
58520 KiB
test_0012.txt
AC
1184 ms
56228 KiB
test_0013.txt
AC
1286 ms
57824 KiB
test_0014.txt
AC
1374 ms
61572 KiB
test_0015.txt
AC
1411 ms
61300 KiB
test_0016.txt
AC
1225 ms
58852 KiB
test_0017.txt
AC
1228 ms
56256 KiB
test_0018.txt
AC
1459 ms
62692 KiB
test_0019.txt
AC
1297 ms
60224 KiB
test_0020.txt
AC
1204 ms
56176 KiB
test_0021.txt
AC
1338 ms
61460 KiB
test_0022.txt
AC
1132 ms
54812 KiB
test_0023.txt
AC
1188 ms
56588 KiB
test_0024.txt
AC
1227 ms
56128 KiB
test_0025.txt
AC
1427 ms
62388 KiB
test_0026.txt
AC
1231 ms
57300 KiB
test_0027.txt
AC
1200 ms
57612 KiB
test_0028.txt
AC
1200 ms
56248 KiB
test_0029.txt
AC
1300 ms
60696 KiB
test_0030.txt
AC
1134 ms
53400 KiB
test_0031.txt
AC
1177 ms
55708 KiB
test_0032.txt
AC
1163 ms
56844 KiB
test_0033.txt
AC
1164 ms
54872 KiB
test_0034.txt
AC
1278 ms
59416 KiB
test_0035.txt
AC
1287 ms
59824 KiB
test_0036.txt
AC
1179 ms
56016 KiB
test_0037.txt
AC
1386 ms
62596 KiB
test_0038.txt
AC
1326 ms
60556 KiB
test_0039.txt
AC
1089 ms
57308 KiB
test_0040.txt
AC
1260 ms
57568 KiB
test_0041.txt
AC
1276 ms
58152 KiB
test_0042.txt
AC
1259 ms
59120 KiB
test_0043.txt
AC
1202 ms
56100 KiB
test_0044.txt
AC
1374 ms
60560 KiB
test_0045.txt
AC
1172 ms
55336 KiB
test_0046.txt
AC
1216 ms
58276 KiB
test_0047.txt
AC
1346 ms
62216 KiB
test_0048.txt
AC
1157 ms
55252 KiB
test_0049.txt
AC
1261 ms
56892 KiB
test_0050.txt
AC
1293 ms
58084 KiB
test_0051.txt
AC
1321 ms
60296 KiB
test_0052.txt
AC
1389 ms
60456 KiB
test_0053.txt
AC
1339 ms
60224 KiB
test_0054.txt
AC
1332 ms
60484 KiB
test_0055.txt
AC
1138 ms
55788 KiB
test_0056.txt
AC
1296 ms
59896 KiB
test_0057.txt
AC
1259 ms
58440 KiB
test_0058.txt
AC
1304 ms
59716 KiB
test_0059.txt
AC
1231 ms
56904 KiB
test_0060.txt
AC
1478 ms
65300 KiB
test_0061.txt
AC
1096 ms
53448 KiB
test_0062.txt
AC
1221 ms
55344 KiB
test_0063.txt
AC
1208 ms
58460 KiB
test_0064.txt
AC
1155 ms
54772 KiB
test_0065.txt
AC
1299 ms
57264 KiB
test_0066.txt
AC
1188 ms
56840 KiB
test_0067.txt
AC
1477 ms
67264 KiB
test_0068.txt
AC
1381 ms
63864 KiB
test_0069.txt
AC
1173 ms
55464 KiB
test_0070.txt
AC
1237 ms
58400 KiB
test_0071.txt
AC
1189 ms
56516 KiB
test_0072.txt
AC
1169 ms
55284 KiB
test_0073.txt
AC
1310 ms
58668 KiB
test_0074.txt
AC
1403 ms
63188 KiB
test_0075.txt
AC
1295 ms
59080 KiB
test_0076.txt
AC
1363 ms
60080 KiB
test_0077.txt
AC
1382 ms
62032 KiB
test_0078.txt
AC
1242 ms
57904 KiB
test_0079.txt
AC
1188 ms
55912 KiB
test_0080.txt
AC
1246 ms
58808 KiB
test_0081.txt
AC
1408 ms
63488 KiB
test_0082.txt
AC
1087 ms
52544 KiB
test_0083.txt
AC
1235 ms
58600 KiB
test_0084.txt
AC
1174 ms
56392 KiB
test_0085.txt
AC
1174 ms
55544 KiB
test_0086.txt
AC
1054 ms
52840 KiB
test_0087.txt
AC
1229 ms
58416 KiB
test_0088.txt
AC
1181 ms
55248 KiB
test_0089.txt
AC
1190 ms
56216 KiB
test_0090.txt
AC
1152 ms
56240 KiB
test_0091.txt
AC
1206 ms
57004 KiB
test_0092.txt
AC
1295 ms
59800 KiB
test_0093.txt
AC
1260 ms
59188 KiB
test_0094.txt
AC
1207 ms
58444 KiB
test_0095.txt
AC
1248 ms
60516 KiB
test_0096.txt
AC
1194 ms
55584 KiB
test_0097.txt
AC
1132 ms
55936 KiB
test_0098.txt
AC
1021 ms
53492 KiB
test_0099.txt
AC
1175 ms
58280 KiB
test_0100.txt
AC
1302 ms
59292 KiB
test_0101.txt
AC
1167 ms
56900 KiB
test_0102.txt
AC
1148 ms
54456 KiB
test_0103.txt
AC
1121 ms
54600 KiB
test_0104.txt
AC
1427 ms
64508 KiB
test_0105.txt
AC
1422 ms
61788 KiB
test_0106.txt
AC
1274 ms
58936 KiB
test_0107.txt
AC
1161 ms
55312 KiB
test_0108.txt
AC
1082 ms
53064 KiB
test_0109.txt
AC
1153 ms
55800 KiB
test_0110.txt
AC
1297 ms
60340 KiB
test_0111.txt
AC
1422 ms
63208 KiB
test_0112.txt
AC
1187 ms
57440 KiB
test_0113.txt
AC
1331 ms
61552 KiB
test_0114.txt
AC
1209 ms
59408 KiB
test_0115.txt
AC
1184 ms
56572 KiB
test_0116.txt
AC
1144 ms
54932 KiB
test_0117.txt
AC
1316 ms
62180 KiB
test_0118.txt
AC
1368 ms
62312 KiB
test_0119.txt
AC
1217 ms
57320 KiB
test_0120.txt
AC
1146 ms
54116 KiB
test_0121.txt
AC
1202 ms
56508 KiB
test_0122.txt
AC
1335 ms
59268 KiB
test_0123.txt
AC
1140 ms
54168 KiB
test_0124.txt
AC
1362 ms
62456 KiB
test_0125.txt
AC
1241 ms
57428 KiB
test_0126.txt
AC
1219 ms
57408 KiB
test_0127.txt
AC
1108 ms
55876 KiB
test_0128.txt
AC
1267 ms
62800 KiB
test_0129.txt
AC
1279 ms
58364 KiB
test_0130.txt
AC
1213 ms
56132 KiB
test_0131.txt
AC
1166 ms
54920 KiB
test_0132.txt
AC
1287 ms
59796 KiB
test_0133.txt
AC
1333 ms
61668 KiB
test_0134.txt
AC
1298 ms
60068 KiB
test_0135.txt
AC
1273 ms
58596 KiB
test_0136.txt
AC
1269 ms
59996 KiB
test_0137.txt
AC
1204 ms
57544 KiB
test_0138.txt
AC
1148 ms
55684 KiB
test_0139.txt
AC
1315 ms
62808 KiB
test_0140.txt
AC
1551 ms
68280 KiB
test_0141.txt
AC
1356 ms
62416 KiB
test_0142.txt
AC
1284 ms
62224 KiB
test_0143.txt
AC
1276 ms
58016 KiB
test_0144.txt
AC
1347 ms
63496 KiB
test_0145.txt
AC
1144 ms
55060 KiB
test_0146.txt
AC
1049 ms
54888 KiB
test_0147.txt
AC
1274 ms
57912 KiB
test_0148.txt
AC
1143 ms
55716 KiB
test_0149.txt
AC
1105 ms
53116 KiB