結果
| 問題 |
No.5022 XOR Printer
|
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2025-07-26 16:29:25 |
| 言語 | C++23 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 1,880 ms / 2,000 ms |
| コード長 | 14,292 bytes |
| コンパイル時間 | 4,748 ms |
| コンパイル使用メモリ | 328,660 KB |
| 実行使用メモリ | 7,716 KB |
| スコア | 4,993,340,776 |
| 最終ジャッジ日時 | 2025-07-26 16:31:07 |
| 合計ジャッジ時間 | 101,516 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge5 |
| 純コード判定しない問題か言語 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 50 |
ソースコード
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3")
#define all(x) (x).begin(),(x).end()
typedef long long ll;
const long long INF = 1LL << 60; // 十分大きな値 (ここでは 2^60 とする)
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <iostream>
#include <iterator>
#include <string_view>
#include <type_traits>
template<class T> void chmin(T& a, T b) {
if (a > b) {
a = b;
}
}
template<class T> void chmax(T& a, T b) {
if (a < b) {
a = b;
}
}
#ifdef LOCAL
# include <debug_print.hpp>
# define debug(...) debug_print::multi_print(#__VA_ARGS__, __VA_ARGS__)
#else
# define debug(...) (static_cast<void>(0))
#endif
int N, T;
vector<vector<int>> A;
int x = 0, y = 0, s = 0;
vector<string> command_list = {"U", "D", "L", "R", "W", "C"};
// 移動可能かどうかを判定
bool can_move(int x, int y, const string& command) {
if (command == "U") {
return x > 0;
} else if (command == "D") {
return x < N - 1;
} else if (command == "L") {
return y > 0;
} else if (command == "R") {
return y < N - 1;
}
return true;
}
// コマンドを実行
void process_command(vector<vector<int>>& board, int& x, int& y, int& s, const string& command) {
if (command == "U") {
x = x - 1;
} else if (command == "D") {
x = x + 1;
} else if (command == "L") {
y = y - 1;
} else if (command == "R") {
y = y + 1;
} else if (command == "W") {
board[x][y] ^= s;
} else if (command == "C") {
s ^= board[x][y];
}
}
// スコアを計算
int calc_score(const vector<vector<int>>& board) {
int score = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
score += board[i][j];
}
}
return score;
}
// 改良された評価関数:現在のスコア + 潜在価値
long long enhanced_score(const vector<vector<int>>& board, int x, int y, int s, int remaining_moves) {
long long current_score = calc_score(board);
// 潜在価値の計算
long long potential = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
int dist = abs(i - x) + abs(j - y);
if (dist < remaining_moves) {
// Write操作での期待上昇値
int after_write = board[i][j] ^ s;
if (after_write > board[i][j]) {
potential += (after_write - board[i][j]) / (1 + dist * 0.1);
}
// Copy操作での期待上昇値
int new_s = s ^ board[i][j];
if (__builtin_popcount(new_s) > __builtin_popcount(s)) {
potential += __builtin_popcount(new_s) * 10;
}
}
}
}
return current_score + potential * 0.3; // 潜在価値の重み調整
}
// 盤面の初期値を保持(焼きなまし時のリセット用)
vector<vector<int>> initial_board;
// 時間計測用
inline long long now() {
return chrono::duration_cast<chrono::milliseconds>(
chrono::steady_clock::now().time_since_epoch())
.count();
}
// 与えられた手列をシミュレートし,スコアを返す。
// 無効な手列なら false を返す。
bool simulate(const vector<string>& seq, long long& score_out) {
vector<vector<int>> board = initial_board; // コピー
int tx = 0, ty = 0, ts = 0;
for (const string& cmd : seq) {
if ((cmd == "U" || cmd == "D" || cmd == "L" || cmd == "R") &&
!can_move(tx, ty, cmd)) {
return false; // 壁に衝突
}
process_command(board, tx, ty, ts, cmd);
}
score_out = calc_score(board);
return true;
}
// Greedy で初期手列を構築(改良版:2手先読み)
vector<string> build_greedy_sequence(int T) {
vector<string> seq;
seq.reserve(T);
vector<vector<int>> board = initial_board;
int cx = 0, cy = 0, cs = 0;
random_device rd;
mt19937 rng(rd());
for (int step = 0; step < T; ++step) {
long long best_score = LLONG_MIN;
string best_cmd = "W";
// 2手先まで読む改良版貪欲
for (const string& cmd1 : command_list) {
if ((cmd1 == "U" || cmd1 == "D" || cmd1 == "L" || cmd1 == "R") && !can_move(cx, cy, cmd1)) continue;
// 1手目を試す
int nx1 = cx, ny1 = cy, ns1 = cs;
vector<vector<int>> tmp1 = board;
process_command(tmp1, nx1, ny1, ns1, cmd1);
long long score1 = enhanced_score(tmp1, nx1, ny1, ns1, T - step - 1);
// 2手先も見る(時間に余裕があるとき)
if (step < T - 1) {
long long best_score2 = score1;
for (const string& cmd2 : command_list) {
if ((cmd2 == "U" || cmd2 == "D" || cmd2 == "L" || cmd2 == "R") && !can_move(nx1, ny1, cmd2)) continue;
int nx2 = nx1, ny2 = ny1, ns2 = ns1;
vector<vector<int>> tmp2 = tmp1;
process_command(tmp2, nx2, ny2, ns2, cmd2);
long long score2 = enhanced_score(tmp2, nx2, ny2, ns2, T - step - 2);
if (score2 > best_score2) {
best_score2 = score2;
}
}
score1 = best_score2 * 0.7 + score1 * 0.3; // 2手先と1手先の重み付き平均
}
if (score1 > best_score) {
best_score = score1;
best_cmd = cmd1;
}
}
// ランダム要素も少し混ぜる
if (rng() % 10 == 0) {
vector<string> cand;
for (const string& cmd : command_list) {
if ((cmd == "U" || cmd == "D" || cmd == "L" || cmd == "R")) {
if (can_move(cx, cy, cmd)) cand.push_back(cmd);
} else {
cand.push_back(cmd);
}
}
uniform_int_distribution<int> uni(0, (int)cand.size() - 1);
best_cmd = cand[uni(rng)];
}
process_command(board, cx, cy, cs, best_cmd);
seq.push_back(best_cmd);
}
return seq;
}
// 任意の状態から貪欲に hand_count 手の手列を生成 (盤面と状態を更新)
vector<string> greedy_from_state(vector<vector<int>>& board, int& cx, int& cy, int& cs, int hand_count, mt19937& rng) {
vector<string> seq;
seq.reserve(hand_count);
uniform_int_distribution<int> uni_dir(0, 5);
for (int step = 0; step < hand_count; ++step) {
long long best_score = LLONG_MIN;
string best_cmd = command_list[uni_dir(rng)]; // fallback
long long base_score = calc_score(board);
for (const string& cmd : command_list) {
if ((cmd == "U" || cmd == "D" || cmd == "L" || cmd == "R") && !can_move(cx, cy, cmd)) continue;
int nx = cx, ny = cy, ns = cs;
vector<vector<int>> tmp = board;
process_command(tmp, nx, ny, ns, cmd);
long long sc = enhanced_score(tmp, nx, ny, ns, hand_count - step - 1);
if (sc > best_score) {
best_score = sc;
best_cmd = cmd;
}
}
if (best_score <= base_score) {
// たまにランダムで揺らす
best_cmd = command_list[uni_dir(rng)];
while ((best_cmd == "U" || best_cmd == "D" || best_cmd == "L" || best_cmd == "R") && !can_move(cx, cy, best_cmd)) {
best_cmd = command_list[uni_dir(rng)];
}
}
process_command(board, cx, cy, cs, best_cmd);
seq.push_back(best_cmd);
}
return seq;
}
// ビームサーチで hand_count 手の手列を構築
vector<string> beam_search_from_state(const vector<vector<int>>& init_board, int x0, int y0, int s0, int hand_count, int beam_width = 200) {
struct Node {
vector<vector<int>> board;
int x, y, s;
long long score;
vector<string> path;
bool operator<(const Node& other) const { // for priority_queue (min-heap)
return score > other.score; // 逆順(高スコア優先)
}
};
vector<Node> beam;
beam.reserve(beam_width);
beam.push_back({init_board, x0, y0, s0, calc_score(init_board), {}});
for (int depth = 0; depth < hand_count; ++depth) {
vector<Node> cand;
cand.reserve(beam.size() * 6);
for (const Node& node : beam) {
for (const string& cmd : command_list) {
if ((cmd == "U" || cmd == "D" || cmd == "L" || cmd == "R") && !can_move(node.x, node.y, cmd)) continue;
Node nxt = node; // コピー
nxt.path.push_back(cmd);
process_command(nxt.board, nxt.x, nxt.y, nxt.s, cmd);
nxt.score = enhanced_score(nxt.board, nxt.x, nxt.y, nxt.s, hand_count - depth - 1);
cand.push_back(move(nxt));
}
}
// 上位 beam_width を残す
nth_element(cand.begin(), cand.begin() + min(beam_width, (int)cand.size()) - 1, cand.end());
cand.resize(min(beam_width, (int)cand.size()));
beam.swap(cand);
}
// 最良ノードを取得
Node* best = &beam[0];
for (Node& n : beam) if (n.score > best->score) best = &n;
return best->path;
}
// 焼きなましで手列を改善
vector<string> simulated_annealing(vector<string> seq, long long best_score, long long time_limit_ms = 1800) {
const long long start_time = now();
const double T0 = 2000.0;
const double T_end = 1e-3;
random_device rd;
mt19937 rng(rd());
uniform_int_distribution<int> cmd_dist(0, (int)command_list.size() - 1);
vector<string> best_seq = seq;
long long iter = 0;
while (now() - start_time < time_limit_ms) {
++iter;
double progress = (double)(now() - start_time) / (double)time_limit_ms;
double temperature = pow(T_end / T0, progress) * T0;
vector<string> new_seq = seq;
long long new_score;
// --- 近傍生成: 3種類の戦略 ---
int r = rng() % 100; // より細かい確率制御
if (r == 0 && now() - start_time < time_limit_ms - 600) { // 1% でビームサーチ (十分な時間がある場合)
uniform_int_distribution<int> pos_dist(0, T - 2);
int l = pos_dist(rng);
int depth = min(T - l, 40); // 深さを少し増加
int width = 50; // ビーム幅も増加
// l までシミュレートして状態取得
vector<vector<int>> board = initial_board;
int cx = 0, cy = 0, cs = 0;
bool is_valid = true;
for (int i = 0; i < l; ++i) {
if (!can_move(cx, cy, seq[i]) && (seq[i] == "U" || seq[i] == "D" || seq[i] == "L" || seq[i] == "R")) {
is_valid = false;
break;
}
process_command(board, cx, cy, cs, seq[i]);
}
if (is_valid) {
vector<string> rebuilt = beam_search_from_state(board, cx, cy, cs, depth, width);
for (int i = 0; i < rebuilt.size(); ++i) new_seq[l + i] = rebuilt[i];
}
} else if (r < 8) { // 7% で大規模再構築 (greedy) - 頻度を下げる
uniform_int_distribution<int> pos_dist(0, T - 2);
int l = pos_dist(rng);
int len = min(T - l, (int)(T / 5)); // 全体の 20% 程度を再構築
// l までシミュレートして状態取得
vector<vector<int>> board = initial_board;
int cx = 0, cy = 0, cs = 0;
for (int i = 0; i < l; ++i) {
if (!can_move(cx, cy, seq[i]) && (seq[i] == "U" || seq[i] == "D" || seq[i] == "L" || seq[i] == "R")) {
// 現在の手列がそもそも無効なら break
board.clear();
break;
}
process_command(board, cx, cy, cs, seq[i]);
}
if (!board.empty()) {
// 再構築部分を greedy で生成
vector<string> rebuilt = greedy_from_state(board, cx, cy, cs, len, rng);
for (int i = 0; i < len; ++i) new_seq[l + i] = rebuilt[i];
}
} else { // 92% で小規模変形 - 細かい調整を重視
// 小規模(従来)のランダム区間書き換え
uniform_int_distribution<int> pos_dist(0, T - 1);
int l = pos_dist(rng);
int len = min(T - l, (int)(pos_dist(rng) % 8 + 1)); // 長さを少し短く
uniform_int_distribution<int> cmd_dist_local(0, (int)command_list.size() - 1);
for (int i = l; i < l + len; ++i) {
new_seq[i] = command_list[cmd_dist_local(rng)];
}
}
if (!simulate(new_seq, new_score)) continue;
long long delta = new_score - best_score;
if (delta > 0 || exp((double)delta / temperature) > (double)rng() / rng.max()) {
seq.swap(new_seq);
best_score = new_score;
best_seq = seq;
// 改善時にログ出力(デバッグ用)
if (delta > 0) {
cerr << "Improvement at iter " << iter << ": " << best_score << " (++" << delta << ")" << endl;
}
}
}
cerr << "SA completed. Iterations: " << iter << ", Best score: " << best_score << endl;
return best_seq;
}
int main() {
// 入力
cin >> N >> T;
A.resize(N, vector<int>(N));
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
cin >> A[i][j];
}
}
// 初期盤面を保存
initial_board = A;
// Greedy で初期手列を生成
vector<string> seq = build_greedy_sequence(T);
long long best_score;
simulate(seq, best_score);
// 焼きなましで改善
seq = simulated_annealing(seq, best_score);
// 出力
for (const string& cmd : seq) {
cout << cmd << '\n';
}
return 0;
}