結果
| 問題 |
No.5022 XOR Printer
|
| コンテスト | |
| ユーザー |
prussian_coder
|
| 提出日時 | 2025-07-25 11:44:46 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 1,457 ms / 2,000 ms |
| コード長 | 31,948 bytes |
| コンパイル時間 | 5,594 ms |
| コンパイル使用メモリ | 261,716 KB |
| 実行使用メモリ | 318,208 KB |
| スコア | 5,227,012,591 |
| 最終ジャッジ日時 | 2025-07-26 12:45:11 |
| 合計ジャッジ時間 | 73,542 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
| 純コード判定しない問題か言語 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 50 |
ソースコード
#pragma GCC optimize "-O3,omit-frame-pointer,inline,unroll-all-loops,fast-math"
#include <iostream>
#include <vector>
#include <tuple>
#include <algorithm>
#include <random>
#include <limits>
#include <cmath>
#include <numeric>
#include <optional>
#include <iterator>
#include <unordered_set>
#include <functional>
using namespace std;
// Random number generator with fixed seed
static mt19937 rng(0);
static uniform_real_distribution<double> uniDist(0.0, 1.0);
// Constants
constexpr int MAX_BIT = 20;
constexpr int MAX_SEE_BIT = 4;
constexpr int DEFAULT_TARGET_BIT = 19;
constexpr double ANNEALING_DECAY = 0.99;
constexpr double INITIAL_ACCEPT_PROB = 0.8;
constexpr int BEAM_WIDTH = 10;
constexpr int MAX_TRIAL = 20;
constexpr int MAX_TRIAL_LAST = 50;
constexpr int MIN_TARGET_BIT = 11; // Use beam search below this
// Global variables
int N, T, N2;
vector<int> board;
vector<int> BINARY_VALS;
int BIT_MASK;
// Forward declarations used by beam search
int manhattan_distance(int pos1, int pos2);
vector<char> generate_move_commands(int start, int end);
//------------------------------------------------------------------------------
// Utility Functions
//------------------------------------------------------------------------------
// 1次元の位置を(x, y)に変換
pair<int, int> to_2d(int pos) {
return { pos / N, pos % N };
}
inline void for_each_near(int pos, const function<void(int)>& f) {
auto [x, y] = to_2d(pos);
for (int dx = -8; dx <= 8; ++dx) {
int nx = x + dx;
if (nx < 0 || nx >= N) continue;
int rem = 8 - abs(dx);
for (int dy = -rem; dy <= rem; ++dy) {
int ny = y + dy;
if (ny < 0 || ny >= N) continue;
f(nx * N + ny);
}
}
}
//------------------------------------------------------------------------------
// Beam search borrowed from sol7.cpp (simplified for reuse)
//------------------------------------------------------------------------------
struct BeamState {
vector<int> board;
int s, pos, turn, score, prev_c_cnt;
BeamState* prev_state;
pair<char,int> prev_action;
BeamState(
const vector<int>& b,
int st,
int p,
int t,
int sc
) : board(b), s(st), pos(p), turn(t), score(sc), prev_c_cnt(0),
prev_state(nullptr), prev_action({'?',0}) {}
};
inline bool beam_state_valid(const BeamState* st, int limit) {
if (st->turn == limit) return true;
int mask = (1 << 19) - 1;
for (int v : st->board) {
mask &= v;
if (mask == 0) return true;
}
return false;
}
inline int beam_last_score(const BeamState* st, int limit) {
int min_v = numeric_limits<int>::max();
int min_pos = 0;
for (int i = 0; i < N2; ++i) {
if (st->board[i] < min_v) {
min_v = st->board[i];
min_pos = i;
}
}
int dist = manhattan_distance(st->pos, min_pos);
if (st->turn + dist + 3 >= limit) return 0;
return st->score + st->s - min_v;
}
inline vector<char> beam_last_move(const BeamState* st) {
int min_v = numeric_limits<int>::max();
int min_pos = 0;
for (int i = 0; i < N2; ++i) {
if (st->board[i] < min_v) {
min_v = st->board[i];
min_pos = i;
}
}
auto cmds = generate_move_commands(st->pos, min_pos);
cmds.push_back('W');
cmds.push_back('C');
cmds.push_back('W');
return cmds;
}
class BeamSearchSolver {
static const int BEAM_WIDTH = 80;
static const int STATE_QUE_SIZE = 10;
int limit;
struct PairHash { size_t operator()(uint64_t x) const { return x; } };
public:
explicit BeamSearchSolver(int lim) : limit(lim) {}
BeamState* process(const vector<int>& init_board, int init_s, int init_pos) {
static BeamState* states[STATE_QUE_SIZE][BEAM_WIDTH];
static int sizes[STATE_QUE_SIZE];
for (int i = 0; i < STATE_QUE_SIZE; ++i) sizes[i] = 0;
auto cmp = [](BeamState* a, BeamState* b) { return a->score > b->score; };
BeamState* initial = new BeamState(
init_board, init_s, init_pos, 0,
accumulate(init_board.begin(), init_board.end(), 0)
);
states[0][0] = initial;
sizes[0] = 1;
push_heap(&states[0][0], &states[0][0] + 1, cmp);
int best_score = 0;
BeamState* best_state = nullptr;
for (int turn = 0; turn < limit; ++turn) {
int idx = turn % STATE_QUE_SIZE;
int cur_sz = sizes[idx];
BeamState* cur[BEAM_WIDTH];
for (int i = 0; i < cur_sz; ++i) cur[i] = states[idx][i];
sizes[idx] = 0;
unordered_set<uint64_t, PairHash> seen;
for (int i = 0; i < cur_sz; ++i) {
BeamState* st = cur[i];
uint64_t key = (uint64_t(st->score) << 16) | uint32_t(st->s/1024);
if (!seen.insert(key).second) continue;
for_each_near(st->pos, [&](int v2) {
try_push(st, 'W', v2, states, sizes, cmp);
if (st->prev_c_cnt < 2)
try_push(st, 'C', v2, states, sizes, cmp);
});
}
for (int i = 0; i < cur_sz; ++i) {
int fs = beam_last_score(cur[i], limit);
if (fs > best_score) {
best_score = fs;
best_state = cur[i];
}
}
}
return best_state;
}
private:
void try_push(
BeamState* st,
char key,
int pos,
BeamState* states[][BEAM_WIDTH],
int sizes[],
const function<bool(BeamState*, BeamState*)>& cmp
) {
int dist = manhattan_distance(st->pos, pos);
int next_turn = st->turn + dist + 1;
if (next_turn > limit) return;
int new_score = (key == 'C') ? st->score
: st->score + ((st->board[pos] ^ st->s) - st->board[pos]);
int idx = next_turn % STATE_QUE_SIZE;
int sz = sizes[idx];
if (sz < BEAM_WIDTH) {
BeamState* ns = apply_move(st, key, pos, next_turn, new_score);
if (beam_state_valid(ns, limit)) {
states[idx][sz] = ns;
++sizes[idx];
push_heap(&states[idx][0], &states[idx][sizes[idx]], cmp);
}
} else if (new_score > states[idx][0]->score) {
BeamState* ns = apply_move(st, key, pos, next_turn, new_score);
if (beam_state_valid(ns, limit)) {
pop_heap(&states[idx][0], &states[idx][BEAM_WIDTH], cmp);
states[idx][BEAM_WIDTH - 1] = ns;
push_heap(&states[idx][0], &states[idx][BEAM_WIDTH], cmp);
}
}
}
BeamState* apply_move(
BeamState* st,
char key,
int pos,
int next_turn,
int new_score
) {
BeamState* ns = new BeamState(*st);
ns->prev_state = st;
ns->prev_action = {key, st->pos};
ns->turn = next_turn;
ns->score = new_score;
if (key == 'C') {
ns->s ^= st->board[pos];
ns->prev_c_cnt = st->prev_c_cnt + 1;
} else {
ns->board[pos] = st->board[pos] ^ st->s;
ns->prev_c_cnt = 0;
}
ns->pos = pos;
return ns;
}
};
vector<char> run_beam_search(
const vector<int>& init_board,
int init_s,
int init_pos,
int rem_turn
) {
BeamSearchSolver solver(rem_turn);
BeamState* best = solver.process(init_board, init_s, init_pos);
vector<char> last_actions = beam_last_move(best);
vector<char> actions;
for (BeamState* cur = best; cur->prev_state != nullptr; cur = cur->prev_state) {
auto [key, last_pos] = cur->prev_action;
actions.push_back(key);
auto mv = generate_move_commands(last_pos, cur->pos);
for (auto it = mv.rbegin(); it != mv.rend(); ++it) actions.push_back(*it);
}
reverse(actions.begin(), actions.end());
actions.insert(actions.end(), last_actions.begin(), last_actions.end());
return actions;
}
// (x, y)座標を1次元インデックスへ変換
int to_1d(int x, int y) {
return x * N + y;
}
// マンハッタン距離を求める
int manhattan_distance(int pos1, int pos2) {
auto [x1, y1] = to_2d(pos1);
auto [x2, y2] = to_2d(pos2);
return abs(x1 - x2) + abs(y1 - y2);
}
// startからendまで移動する操作列を生成
vector<char> generate_move_commands(int start, int end) {
vector<char> commands;
auto [x1, y1] = to_2d(start);
auto [x2, y2] = to_2d(end);
int cx = x1;
int cy = y1;
// Vertical moves
while (cx < x2) { commands.push_back('D'); ++cx; }
while (cx > x2) { commands.push_back('U'); --cx; }
// Horizontal moves
while (cy < y2) { commands.push_back('R'); ++cy; }
while (cy > y2) { commands.push_back('L'); --cy; }
return commands;
}
// 移動操作列をコマンドベクタへ追加するヘルパー
void append_move_commands(vector<char>& out, int start, int end) {
auto mv = generate_move_commands(start, end);
out.insert(out.end(), mv.begin(), mv.end());
}
// 操作列を順に適用しボードと状態を更新
void process_board(
vector<int>& b,
const vector<char>& commands,
int& s,
int& pos
) {
for (char c : commands) {
switch (c) {
case 'W': b[pos] ^= s; break;
case 'C': s ^= b[pos]; break;
case 'D': pos += N; break;
case 'U': pos -= N; break;
case 'R': pos += 1; break;
case 'L': pos -= 1; break;
}
}
}
// process_board の逆操作を一手だけ行う
void revert_board(
vector<int>& b,
char c,
int& s,
int& pos
) {
switch (c) {
case 'W': b[pos] ^= s; break;
case 'C': s ^= b[pos]; break;
case 'D': pos -= N; break;
case 'U': pos += N; break;
case 'R': pos -= 1; break;
case 'L': pos += 1; break;
}
}
// start_pos と end_pos を含む長方形領域内の中間点を列挙
vector<int> get_one_intermediate_positions_2d(
int start_pos,
int end_pos
) {
auto [sx, sy] = to_2d(start_pos);
auto [ex, ey] = to_2d(end_pos);
if (sx > ex) swap(sx, ex);
if (sy > ey) swap(sy, ey);
vector<int> candidates;
for (int x = sx; x <= ex; ++x) {
for (int y = sy; y <= ey; ++y) {
if (x != ex || y != ey) {
candidates.push_back(to_1d(x, y));
}
}
}
return candidates;
}
// 1次元移動において2点の中間候補を取得
vector<pair<int, int>> get_two_intermediate_positions_1d(
int start,
int end
) {
int left, right;
if (start <= end) {
left = start;
right = end;
} else {
left = -start;
right = -end;
}
vector<pair<int,int>> result;
for (int m1 = left; m1 <= right; ++m1) {
for (int m2 = m1; m2 <= right; ++m2) {
result.emplace_back(abs(m1), abs(m2));
}
}
return result;
}
// 2次元移動で経由しうる2点の組み合わせを取得
vector<pair<int, int>> get_two_intermediate_positions_2d(
int start_pos,
int end_pos
) {
auto [sx, sy] = to_2d(start_pos);
auto [ex, ey] = to_2d(end_pos);
auto x_pairs = get_two_intermediate_positions_1d(sx, ex);
auto y_pairs = get_two_intermediate_positions_1d(sy, ey);
vector<pair<int,int>> candidates;
for (auto& xp : x_pairs) {
for (auto& yp : y_pairs) {
int pos1 = to_1d(xp.first, yp.first);
int pos2 = to_1d(xp.second, yp.second);
if (pos1 != pos2 && pos2 != end_pos) {
candidates.emplace_back(pos1, pos2);
}
}
}
return candidates;
}
// target_bit のビットが目的範囲に収まっているか判定
bool is_valid_state(int state, int target_bit) {
return (BINARY_VALS[target_bit] <= state &&
state < BINARY_VALS[target_bit + 1]);
}
//------------------------------------------------------------------------------
// RouteOptimizer: 2-opt 山登り法で経路を改良するクラス
//------------------------------------------------------------------------------
class RouteOptimizer {
public:
// 与えられた盤面で target_bit が0のマスを巡回する経路を生成
vector<int> generate_optimal_route(
int start_pos,
const vector<int>& brd,
int target_bit
) {
auto targets = find_target_positions(brd, target_bit);
auto route = build_nearest_neighbor_route(
start_pos,
brd,
move(targets),
target_bit
);
route = apply_2opt_optimization(move(route), brd, target_bit);
return route;
}
private:
// target_bit が0の位置一覧を取得
vector<int> find_target_positions(
const vector<int>& brd,
int target_bit
) {
vector<int> res;
res.reserve(N2);
for (int i = 0; i < N2; ++i) {
if (target_bit <= 17 && ~(brd[i] >> 18) & 1) {
continue;
}
if (((brd[i] >> target_bit) & 1) == 0) {
res.push_back(i);
}
}
return res;
}
// 貪欲法で近傍を順に訪問する経路を構築
vector<int> build_nearest_neighbor_route(
int start_pos,
const vector<int>& brd,
vector<int> targets,
int target_bit
) {
vector<int> route = { start_pos };
int cur = start_pos;
unordered_set<int> rem(targets.begin(), targets.end());
while (!rem.empty()) {
int best = -1;
int bd = numeric_limits<int>::max();
for (int t : rem) {
int d = manhattan_distance(cur, t);
if (d < bd) {
bd = d;
best = t;
}
}
route.push_back(best);
rem.erase(best);
cur = best;
}
adjust_final_position(route, brd, target_bit);
return route;
}
// 最後に回る位置について、target_bitより大きいbit桁が1になるように修正する
void adjust_final_position(
vector<int>& route,
const vector<int>& brd,
int target_bit
) {
for (int i = (int)route.size() - 1; i >= 0; --i) {
if (can_be_final_position(
brd[route[i]],
target_bit
)) {
swap(route[i], route.back());
return;
}
}
}
// 2-opt 法で経路を改良(山登りベースだが、スコア変化が0の場合は低確率で変更許容する)
vector<int> apply_2opt_optimization(
vector<int> route,
const vector<int>& brd,
int target_bit
) {
float acc = INITIAL_ACCEPT_PROB;
int L = route.size();
while (true) {
bool improved = false;
vector<int> idx(L+1);
iota(idx.begin(), idx.end(), 0);
shuffle(idx.begin(), idx.end(), rng);
for (int a = 1; a < L && !improved; ++a) {
for (int b = a + 1; b <= L && !improved; ++b) {
int i = idx[a];
int j = idx[b];
if (i > j) swap(i, j);
if (i == 0) {
continue;
}
if (!is_valid_2opt_move(route, brd, i, j, target_bit)) {
continue;
}
int delta = calculate_2opt_improvement(
route, i, j
);
if (delta < 0 ||
(delta == 0 && uniDist(rng) < acc)) {
reverse(
route.begin() + i,
route.begin() + j
);
improved = true;
}
}
}
if (!improved) {
break;
}
acc *= ANNEALING_DECAY;
}
return route;
}
// 終点との整合性を考慮して 2-opt 移動が妥当か判定
// 終点が最後に回る位置の場合は、target_bitより大きいbit桁が1である必要がある
bool is_valid_2opt_move(
const vector<int>& route,
const vector<int>& brd,
int i,
int j,
int tb
) {
if (j == (int)route.size() &&
!can_be_final_position(
brd[route[i]],
tb
)) {
return false;
}
return true;
}
// 2-opt による距離改善量を計算
int calculate_2opt_improvement(
const vector<int>& route,
int i,
int j
) {
int L = route.size();
if (j == L) {
return manhattan_distance(route[i - 1], route[j - 1]) -
manhattan_distance(route[i - 1], route[i]);
}
return manhattan_distance(route[i - 1], route[j - 1]) +
manhattan_distance(route[i], route[j]) -
manhattan_distance(route[i - 1], route[i]) -
manhattan_distance(route[j - 1], route[j]);
}
// 指定位置を最終地点にできるか判定
bool can_be_final_position(
int cell_val,
int tb
) {
return (tb == DEFAULT_TARGET_BIT) ||
is_valid_state(cell_val ^ BIT_MASK, tb);
}
};
//------------------------------------------------------------------------------
// WriteCopyPlanner: 経路に沿った Write/Copy 操作列を動的計画法で構築する
//------------------------------------------------------------------------------
class WriteCopyPlanner {
public:
// target_bit と探索に用いるビット数を初期化
WriteCopyPlanner(int tb)
: target_bit(tb)
, see_bit(min(tb, MAX_SEE_BIT))
{}
// target_bit未満の上位see_bit桁について、上位から連続して1が立っている数を数える
// Write操作をした際に、target_bit未満の次のbitが1であれば、次のbitを見たときに回らなくて済むのでお得であるため
int count_leading_ones(int v) const {
int cnt = 0;
for (int k = see_bit - 1; k >= 0; --k) {
if (!((v >> k) & 1)) {
break;
}
++cnt;
}
return cnt;
}
// prev_pos→p1→p2→posへ移動する際に、移動中の数字p1,p2を2つ使用してsの値を更新できるか判定
// 2つの数値は、target_bit以上のbit桁が1である必要がある。p1 == prev_posの場合は、p1がp1^sに更新されていることに注意
optional<int> compute_copy_state(
int p1,
int p2,
int prev_pos,
int st,
const vector<int>& brd,
const vector<int>& tops
) const {
int xor1 = brd[p1] ^ BIT_MASK;
int xor2 = brd[p2] ^ BIT_MASK;
if (p1 == prev_pos) {
if (
xor2 >= (1 << target_bit) ||
!is_valid_state(brd[p1] ^ brd[p2], target_bit)
) {
return nullopt;
}
return tops[p1] ^ tops[p2];
}
if (xor1 >= (1 << target_bit) || xor2 >= (1 << target_bit)) {
return nullopt;
}
return st ^ tops[p1] ^ tops[p2];
}
// 経路に沿った最適な Write/Copy コマンド列を生成
vector<char> generate_execution_plan(
const vector<int>& route,
const vector<int>& brd,
int init_state
) {
int L = route.size();
int S = 1 << see_bit;
// DP tables
vector<vector<float>> dp(
L - 1,
vector<float>(S, numeric_limits<float>::infinity())
);
// 復元目的のためにどこから来たかを記録する
vector<vector<tuple<int,int,int>>> tr(
L - 1,
vector<tuple<int,int,int>>(S, { -1, -1, -1 })
);
// 最初のcopy操作について、どのセルを使用したかを記録しておく
vector<int> start_pos(S, -1);
int sp = route[0];
// 現在の地点付近のセルを訪問・コピーし、sのtarget_bitより大きい桁を0、target_bitを1になるようにする
for (int p = 0; p < N2; ++p) {
int xor_val = brd[p] ^ init_state;
if (!is_valid_state(xor_val, target_bit)) continue;
int st =
(xor_val % (1 << target_bit)) >>
(target_bit - see_bit);
int dist =
manhattan_distance(sp, p) +
manhattan_distance(p, route[1]);
if (dp[0][st] > dist) {
dp[0][st] = dist;
start_pos[st] = p;
}
}
// target_bit未満の上位see_bit桁のみDPで考慮する
vector<int> tops(N2);
for (int i = 0; i < N2; ++i) {
tops[i] =
(brd[i] % (1 << target_bit)) >>
(target_bit - see_bit);
}
// DPテーブルを埋める(最終ターンは除く)
for (int i = 1; i < L - 1; ++i) {
for (int st = 0; st < S; ++st) {
if (dp[i - 1][st] == numeric_limits<float>::infinity()) {
continue;
}
// Copy操作せずに移動する場合
{
int val = tops[route[i]] ^ st;
int score = count_leading_ones(val);
float cost = dp[i - 1][st] - score;
if (dp[i][st] > cost) {
dp[i][st] = cost;
tr[i][st] = { st, -1, -1 };
}
}
// route[i-1]→p1→p2→route[i]へ移動する際に、途中のp1,p2を2つをCopyしてsを変更する
if (i > 1) {
for (auto& pr :
get_two_intermediate_positions_2d(
route[i - 1],
route[i]
)) {
auto [p1, p2] = pr;
auto ns = compute_copy_state(
p1,
p2,
route[i - 1],
st,
brd,
tops
);
if (!ns) {
continue;
}
int new_st = *ns;
int val = tops[route[i]] ^ new_st;
int score = count_leading_ones(val);
float cost = dp[i - 1][st] + 2 - score;
if (dp[i][new_st] > cost) {
dp[i][new_st] = cost;
tr[i][new_st] = { st, p1, p2 };
}
}
}
}
}
// 経路を復元する
vector<char> cmds;
int final_idx = L - 2;
int best_st =
min_element(
dp[final_idx].begin(),
dp[final_idx].end()
) - dp[final_idx].begin();
for (int i = final_idx; i >= 2; --i) {
auto [prev_st, p1, p2] = tr[i][best_st];
cmds.push_back('W');
if (p1 < 0) {
append_move_commands(cmds, route[i - 1], route[i]);
} else {
append_move_commands(cmds, p2, route[i]);
cmds.push_back('C');
append_move_commands(cmds, p1, p2);
cmds.push_back('C');
append_move_commands(cmds, route[i - 1], p1);
}
best_st = prev_st;
}
// 最初のcopy操作を追加する
cmds.push_back('W');
int init_pos = start_pos[best_st];
append_move_commands(cmds, init_pos, route[1]);
cmds.push_back('C');
append_move_commands(cmds, route[0], init_pos);
// 経路を逆順にする
reverse(cmds.begin(), cmds.end());
// 最後の移動を追加する
append_move_commands(cmds, route[L - 2], route[L - 1]);
cmds.push_back(
(target_bit == DEFAULT_TARGET_BIT) ? 'W' : 'C'
);
return cmds;
}
private:
int target_bit;
int see_bit;
};
//------------------------------------------------------------------------------
// State: ビームサーチで保持する盤面・位置・操作列
//------------------------------------------------------------------------------
class State {
public:
vector<char> commands;
vector<int> board;
int s;
int pos;
int target_bit;
// 状態を保持する単純なデータコンテナ
State(
const vector<char>& cmds,
const vector<int>& brd,
int state,
int position,
int tb
) : commands(cmds), board(brd), s(state), pos(position), target_bit(tb) {}
// 現在のボード合計を計算
int get_score() const {
return accumulate(board.begin(), board.end(), 0);
}
// 処理手数 - target_bit未満のleading_oneの数
int get_estimated_cost() const {
int cost = commands.size();
for (int val : board) {
for (int b = target_bit - 2; b >= 0; --b) {
if ((val >> b) & 1) {
cost -= 1;
} else {
break;
}
}
}
return cost;
}
};
//------------------------------------------------------------------------------
// Postprocess and Main Solver
//------------------------------------------------------------------------------
// このままでは18ビット目が0であるセルが残ってしまうので、操作終了時にこのセルの値とsの値を交換することで、18ビット目が1になるようにしたい
// 操作列 + この処理を加えた操作(移動操作 + C + W + C + W)がT手以下に収まるようにcmdを戻す
void postprocess_board(
vector<int>& b,
vector<char>& cmds,
int& s,
int& pos
) {
while (true) {
int min_pos =
min_element(b.begin(), b.end()) - b.begin();
int steps =
cmds.size() +
manhattan_distance(pos, min_pos) +
4;
if (steps > T) {
char last_cmd = cmds.back();
cmds.pop_back();
revert_board(b, last_cmd, s, pos);
continue;
}
int best_s = numeric_limits<int>::min();
int best_v = -1;
for (int v : get_one_intermediate_positions_2d(pos, min_pos)) {
int s2 = b[v] ^ s;
if (s2 > best_s) {
best_s = s2;
best_v = v;
}
}
if (best_s < 1000000) {
char last_cmd = cmds.back();
cmds.pop_back();
revert_board(b, last_cmd, s, pos);
continue;
}
// 新しい操作列を追加する
auto mv1 = generate_move_commands(pos, best_v);
append_move_commands(cmds, pos, best_v);
cmds.push_back('C');
auto mv2 = generate_move_commands(best_v, min_pos);
append_move_commands(cmds, best_v, min_pos);
cmds.push_back('W');
cmds.push_back('C');
cmds.push_back('W');
vector<char> recent(
cmds.end() -
(mv1.size() + 1 + mv2.size() + 3),
cmds.end()
);
process_board(b, recent, s, pos);
return;
}
}
// ビームサーチで操作列を生成する
void solve_puzzle() {
int pos = to_1d(0, 0);
int s = 0;
int trial_count;
vector<char> cmds;
vector<State> states = { State(cmds, board, s, pos, MAX_BIT) };
// 上位bit桁から順に、target_bitより大きい桁が1になるように操作列を生成する
for (int tb = MAX_BIT - 1; tb >= MIN_TARGET_BIT; --tb) {
cerr << tb << " " << states[0].commands.size() << endl;
int last_cmd_size = states[0].commands.size();
vector<State> next_states;
for (const auto& state : states) {
if (last_cmd_size > 930) {
trial_count = MAX_TRIAL_LAST;
} else {
trial_count = MAX_TRIAL;
}
for (int trial = 0; trial < trial_count; ++trial) {
vector<char> commands = state.commands;
int current_pos = state.pos;
int current_s = state.s;
vector<int> current_board = state.board;
// routeを生成する
RouteOptimizer ro;
auto route = ro.generate_optimal_route(current_pos, current_board, tb);
// 経路に沿った操作列を生成する
WriteCopyPlanner wcp(tb);
auto exec_cmds =
wcp.generate_execution_plan(route, current_board, current_s);
// 操作列を適用する
process_board(current_board, exec_cmds, current_s, current_pos);
commands.insert(commands.end(), exec_cmds.begin(), exec_cmds.end());
next_states.emplace_back(commands, current_board, current_s, current_pos, tb);
}
}
if (!next_states.empty() && (int)next_states[0].commands.size() > T){
states = next_states;
break;
};
// コストが低い順にソートし、上位BEAM_WIDTH個を選抜する
sort(next_states.begin(), next_states.end(),
[](const State& a, const State& b) {
return a.get_estimated_cost() < b.get_estimated_cost();
});
states.clear();
int keep_count = min(BEAM_WIDTH, (int)next_states.size());
for (int i = 0; i < keep_count; ++i) {
states.push_back(next_states[i]);
}
}
// After main loop, use beam search for remaining turns
int best_score = 0;
vector<char> best_cmds;
for (const auto& state : states) {
board = state.board;
cmds = state.commands;
s = state.s;
pos = state.pos;
int remain = T - (int)cmds.size();
if (remain > 0) {
auto extra = run_beam_search(board, s, pos, remain);
process_board(board, extra, s, pos);
cmds.insert(cmds.end(), extra.begin(), extra.end());
}
postprocess_board(board, cmds, s, pos);
int score = accumulate(board.begin(), board.end(), 0);
if (score > best_score) {
best_score = score;
best_cmds = cmds;
}
}
cmds = best_cmds;
// 出力
for (int i = 0; i < T && i < (int)cmds.size(); ++i) {
cout << cmds[i] << "\n";
}
}
// 入力を読み込み solve_puzzle を呼び出すエントリポイント
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> N >> T;
N2 = N * N;
board.resize(N2);
for (int i = 0; i < N2; ++i) {
cin >> board[i];
}
BINARY_VALS.resize(MAX_BIT + 1);
for (int i = 0; i <= MAX_BIT; ++i) {
BINARY_VALS[i] = 1 << i;
}
BIT_MASK = (1 << MAX_BIT) - 1;
solve_puzzle();
return 0;
}
prussian_coder