結果

問題 No.3738 Right Hamiltonian
コンテスト
ユーザー 👑 kencho
提出日時 2026-08-04 11:32:09
言語 C++23
(gcc 15.3.0 + boost 1.92.0 + ACL)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
TLE  
実行時間 -
コード長 10,989 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 3,724 ms
コンパイル使用メモリ 356,948 KB
実行使用メモリ 6,528 KB
最終ジャッジ日時 2026-09-19 12:38:20
合計ジャッジ時間 9,886 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
サブタスク 配点 結果
部分点 80 % AC * 10 TLE * 1 -- * 27
満点 20 % AC * 10 TLE * 1 -- * 49
合計 4 * 0% = 0 点
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <bits/stdc++.h>
using namespace std;

// 迷路固有の構造は使わず、通る順番を DFS で全探索する。
// 未訪問部分の連結性と次数による一般的な Hamilton 路の枝刈りだけを行う。

int H, W, floor_count;
vector<string> grid_data;
vector<unsigned char> used;
vector<unsigned char> floor_cell;
vector<unsigned char> cell_color;
vector<int> path;
vector<int> floor_cells;
vector<array<int, 4>> next_cell;
vector<int> bfs_queue;
vector<unsigned int> reached;
unsigned int reach_stamp;
int unused_color_count[2];
long long searched_nodes;
long long node_limit;

constexpr int DR[4] = {-1, 0, 1, 0};
constexpr int DC[4] = {0, 1, 0, -1};
constexpr char DIR_NAME[5] = "URDL";

int cell_id(int r, int c) {
    return r * W + c;
}

int row_of(int p) {
    return p / W;
}

int col_of(int p) {
    return p % W;
}

int move_to(int p, int direction) {
    int r = row_of(p) + DR[direction];
    int c = col_of(p) + DC[direction];
    if (r < 0 || r >= H || c < 0 || c >= W) return -1;
    return cell_id(r, c);
}

bool is_unused_floor(int p) {
    return p >= 0 && floor_cell[p] && !used[p];
}

void visit(int p) {
    used[p] = 1;
    --unused_color_count[cell_color[p]];
}

void unvisit(int p) {
    used[p] = 0;
    ++unused_color_count[cell_color[p]];
}

// 現在地から次に使える辺を allowed_next に入れたとき、残りに
// Hamilton 路が存在するための必要条件を確認する。
bool can_finish(int current, int direction, bool direction_fixed, int visited_count) {
    int remaining = floor_count - visited_count;
    if (remaining == 0) return true;

    // グリッドは二部グラフなので、残りの路でも白黒のマス数は交互になる。
    int current_color = cell_color[current];
    int color_count[2] = {
        unused_color_count[0] + (current_color == 0),
        unused_color_count[1] + (current_color == 1),
    };
    if (remaining % 2 == 1) {
        if (color_count[0] != color_count[1]) return false;
    } else {
        if (color_count[current_color] != color_count[current_color ^ 1] + 1)
            return false;
    }

    array<int, 4> allowed_next{};
    int allowed_count = 0;

    if (direction_fixed) {
        for (int next_direction : {direction, (direction + 1) % 4}) {
            int q = next_cell[current][next_direction];
            if (is_unused_floor(q)) allowed_next[allowed_count++] = q;
        }
    } else {
        for (int next_direction = 0; next_direction < 4; ++next_direction) {
            int q = next_cell[current][next_direction];
            if (is_unused_floor(q)) allowed_next[allowed_count++] = q;
        }
    }

    if (allowed_count == 0) return false;

    // 現在地を離れた後に戻ることはできないため、未訪問部分だけで連結である必要がある。
    if (++reach_stamp == 0) {
        fill(reached.begin(), reached.end(), 0);
        ++reach_stamp;
    }
    int head = 0, tail = 0;
    bfs_queue[tail++] = allowed_next[0];
    reached[allowed_next[0]] = reach_stamp;
    int degree_one = 0;

    while (head < tail) {
        int p = bfs_queue[head++];
        int degree = 0;
        for (int d = 0; d < 4; ++d) {
            int q = next_cell[p][d];
            if (is_unused_floor(q)) {
                ++degree;
                if (reached[q] != reach_stamp) {
                    reached[q] = reach_stamp;
                    bfs_queue[tail++] = q;
                }
            }
        }
        for (int i = 0; i < allowed_count; ++i) {
            if (allowed_next[i] == p) {
                ++degree;
                break;
            }
        }
        if (degree == 0) return false;
        if (degree == 1 && ++degree_one >= 2) return false;
    }

    if (tail != remaining) return false;

    return true;
}

enum class SearchResult {
    found,
    impossible,
    interrupted,
};

SearchResult dfs(int current, int direction, int visited_count) {
    if (visited_count == floor_count) return SearchResult::found;
    if (++searched_nodes > node_limit) return SearchResult::interrupted;
    if (!can_finish(current, direction, true, visited_count))
        return SearchResult::impossible;

    // 同じ方向へ進む部分を 1 区間として扱う。
    // 「何マス直進してから右折するか」を長い方から全探索するので、
    // 1 マスごとの F/R の DFS と探索範囲は同じである。
    vector<int> straight_cells;
    int p = current;
    while (true) {
        int q = next_cell[p][direction];
        if (!is_unused_floor(q)) break;
        straight_cells.push_back(q);
        visit(q);
        path.push_back(q);
        p = q;
    }

    int right_direction = (direction + 1) % 4;
    bool interrupted = false;
    for (int straight_count = straight_cells.size(); straight_count >= 0;
         --straight_count) {
        int endpoint = straight_count == 0
                     ? current
                     : straight_cells[straight_count - 1];
        int count = visited_count + straight_count;

        if (count == floor_count) return SearchResult::found;

        int q = next_cell[endpoint][right_direction];
        if (!interrupted && is_unused_floor(q)) {
            visit(q);
            path.push_back(q);

            SearchResult result = dfs(q, right_direction, count + 1);
            if (result == SearchResult::found) return SearchResult::found;
            if (result == SearchResult::interrupted) interrupted = true;

            path.pop_back();
            unvisit(q);
        }

        if (straight_count > 0) {
            path.pop_back();
            unvisit(straight_cells[straight_count - 1]);
        }
    }

    return interrupted ? SearchResult::interrupted : SearchResult::impossible;
}

SearchResult search_from(int start) {
    visit(start);
    path.clear();
    path.push_back(start);

    if (!can_finish(start, 0, false, 1)) {
        unvisit(start);
        return SearchResult::impossible;
    }

    for (int direction = 0; direction < 4; ++direction) {
        int q = next_cell[start][direction];
        if (!is_unused_floor(q)) continue;

        visit(q);
        path.push_back(q);
        SearchResult result = dfs(q, direction, 2);
        if (result == SearchResult::found) return SearchResult::found;
        path.pop_back();
        unvisit(q);

        if (result == SearchResult::interrupted) {
            unvisit(start);
            return SearchResult::interrupted;
        }
    }

    unvisit(start);
    return SearchResult::impossible;
}

void print_answer() {
    if (floor_count == 1) {
        cout << row_of(path[0]) + 1 << ' ' << col_of(path[0]) + 1 << " U\n";
        cout << "0\n\n";
        return;
    }

    int initial_direction = -1;
    for (int d = 0; d < 4; ++d) {
        if (move_to(path[0], d) == path[1]) initial_direction = d;
    }

    string commands;
    commands.reserve(floor_count - 1);
    int direction = initial_direction;

    for (int i = 1; i < floor_count; ++i) {
        int next_direction = -1;
        for (int d = 0; d < 4; ++d) {
            if (move_to(path[i - 1], d) == path[i]) next_direction = d;
        }
        commands.push_back(next_direction == direction ? 'F' : 'R');
        direction = next_direction;
    }

    cout << row_of(path[0]) + 1 << ' ' << col_of(path[0]) + 1 << ' '
         << DIR_NAME[initial_direction] << '\n';
    cout << commands.size() << '\n' << commands << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> H >> W;
    grid_data.resize(H);
    for (string &row : grid_data) cin >> row;

    floor_count = 0;
    int color_count[2] = {0, 0};
    for (int r = 0; r < H; ++r) {
        for (int c = 0; c < W; ++c) {
            if (grid_data[r][c] == '.') {
                ++floor_count;
                ++color_count[(r + c) & 1];
                floor_cells.push_back(cell_id(r, c));
            }
        }
    }

    if (abs(color_count[0] - color_count[1]) > 1) {
        cout << -1 << '\n';
        return 0;
    }

    used.assign(H * W, 0);
    floor_cell.assign(H * W, 0);
    cell_color.resize(H * W);
    for (int p = 0; p < H * W; ++p) {
        floor_cell[p] = grid_data[row_of(p)][col_of(p)] == '.';
        cell_color[p] = (row_of(p) + col_of(p)) & 1;
    }
    next_cell.resize(H * W);
    for (int p = 0; p < H * W; ++p) {
        for (int d = 0; d < 4; ++d)
            next_cell[p][d] = move_to(p, d);
    }
    bfs_queue.resize(H * W);
    reached.assign(H * W, 0);
    unused_color_count[0] = color_count[0];
    unused_color_count[1] = color_count[1];
    path.reserve(floor_count);

    if (floor_count == 1) {
        path.push_back(floor_cells[0]);
        print_answer();
        return 0;
    }

    // 一般の Hamilton 路でも必要な、入力全体に対する簡単な次数判定。
    int degree_one = 0;
    for (int p : floor_cells) {
        int degree = 0;
        for (int d = 0; d < 4; ++d) {
            int q = next_cell[p][d];
            if (q >= 0 && floor_cell[q]) ++degree;
        }
        if (degree == 0) {
            cout << -1 << '\n';
            return 0;
        }
        if (degree == 1) ++degree_one;
    }
    if (degree_one > 2) {
        cout << -1 << '\n';
        return 0;
    }

    vector<int> starts = floor_cells;
    stable_sort(starts.begin(), starts.end(), [](int a, int b) {
        auto degree = [](int p) {
            int result = 0;
            for (int d = 0; d < 4; ++d) {
                int q = next_cell[p][d];
                if (q >= 0 && floor_cell[q]) ++result;
            }
            return result;
        };
        return degree(a) < degree(b);
    });

    // 1 つの開始マスだけに長時間を使わないよう、探索ノード上限を
    // 段階的に増やす。解なしと証明できた開始マスは次回以降省く。
    vector<unsigned char> exhausted(starts.size(), 0);
    node_limit = 256;

    while (true) {
        bool all_exhausted = true;

        for (int i = 0; i < static_cast<int>(starts.size()); ++i) {
            if (exhausted[i]) continue;

            searched_nodes = 0;
            SearchResult result = search_from(starts[i]);
            if (result == SearchResult::found) {
#ifdef LOCAL
                cerr << "start=" << row_of(starts[i]) + 1 << ','
                     << col_of(starts[i]) + 1
                     << " limit=" << node_limit
                     << " nodes=" << searched_nodes << '\n';
#endif
                print_answer();
                return 0;
            }
            if (result == SearchResult::impossible) {
                exhausted[i] = 1;
            } else {
                all_exhausted = false;
            }
        }

        if (all_exhausted) break;
        if (node_limit <= numeric_limits<long long>::max() / 4)
            node_limit *= 4;
        else
            node_limit = numeric_limits<long long>::max();
    }

    cout << -1 << '\n';
    return 0;
}
0