結果

問題 No.5022 XOR Printer
ユーザー ymym_tymblack
提出日時 2025-07-26 16:57:49
言語 C++23
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 15,983 bytes
コンパイル時間 4,955 ms
コンパイル使用メモリ 328,048 KB
実行使用メモリ 8,772 KB
スコア 0
最終ジャッジ日時 2025-07-26 17:00:31
合計ジャッジ時間 114,308 ms
ジャッジサーバーID
(参考情報)
judge1 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other TLE * 50
権限があれば一括ダウンロードができます

ソースコード

diff #

#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, const vector<vector<int>>& initial_board) {
    long long current_score = calc_score(board);
    
    // 潜在価値の計算
    long long potential = 0;

    // アイデア1: sの状態評価
    if (s == 0) {
        potential -= 5000; // Cが無意味な状況を避けるペナルティ
    } else {
        potential += __builtin_popcount(s) * 100; // sが強い(多くのビットが立っている)状態を評価
    }

    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) {
                // アイデア2: 未訪問(未変更)セルへのボーナス
                if (board[i][j] == initial_board[i][j]) {
                    potential += 1000 / (1 + dist);
                }

                // 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; // グローバルからは削除し、mainで管理

// 時間計測用
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, const vector<vector<int>>& initial_board) {
    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, const vector<vector<int>>& initial_board) {
    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, initial_board);
            
            // 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, initial_board);
                    
                    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, const vector<vector<int>>& initial_board) {
    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, initial_board);
            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_arg, int x0, int y0, int s0, int hand_count, const vector<vector<int>>& initial_board, 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_arg, x0, y0, s0, enhanced_score(init_board_arg, x0, y0, s0, hand_count, initial_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, initial_board);
                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, const vector<vector<int>>& initial_board, 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, initial_board);
                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, initial_board);
                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, initial_board)) 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];
        }
    }

    vector<vector<int>> initial_board = A;

    // アイデア5: 複数回初期解を生成し、最良のものを選択
    vector<string> best_initial_seq;
    long long best_initial_score = -1;

    int num_initial_solutions = 5;
    cerr << "Generating " << num_initial_solutions << " initial solutions..." << endl;
    for (int i = 0; i < num_initial_solutions; ++i) {
        vector<string> current_seq = build_greedy_sequence(T, initial_board);
        long long current_score;
        if (simulate(current_seq, current_score, initial_board)) {
            if (current_score > best_initial_score) {
                best_initial_score = current_score;
                best_initial_seq = current_seq;
            }
        }
        cerr << "  Solution " << i + 1 << " score: " << current_score << endl;
    }
    cerr << "Best initial score: " << best_initial_score << endl;

    // 焼きなましで改善
    vector<string> final_seq = simulated_annealing(best_initial_seq, best_initial_score, initial_board);

    // 出力
    for (const string& cmd : final_seq) {
        cout << cmd << '\n';
    }

    return 0;
} 
0