提出 #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 |
| 結果 |
|
| セット名 |
テストケース |
| 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 |