結果

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

ソースコード

diff #
raw source code

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

// 部分点 2
// 外周全体が床なら、使わない外周辺を 1 本決めることで内部を 2 長方形に分割できる。
// 各候補について 2 本の渦を 1 マスずつ実際に辿るため、計算量は O(HW(H+W))。

int H, W, floor_count;
int top_row, bottom_row, left_col, right_col;
vector<string> grid_data;
vector<unsigned char> used;

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

struct Rect {
    int top, bottom, left, right;
};

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);
}

int direction_of(int from, int to) {
    for (int d = 0; d < 4; ++d) {
        if (move_to(from, d) == to) return d;
    }
    return -1;
}

bool is_floor(int p) {
    return p >= 0 && grid_data[row_of(p)][col_of(p)] == '.';
}

bool inside(const Rect &rect, int p) {
    if (p < 0) return false;
    int r = row_of(p), c = col_of(p);
    return rect.top <= r && r <= rect.bottom &&
           rect.left <= c && c <= rect.right;
}

vector<int> bounding_perimeter() {
    vector<int> result;
    for (int c = left_col; c < right_col; ++c)
        result.push_back(cell_id(top_row, c));
    for (int r = top_row; r < bottom_row; ++r)
        result.push_back(cell_id(r, right_col));
    for (int c = right_col; c > left_col; --c)
        result.push_back(cell_id(bottom_row, c));
    for (int r = bottom_row; r > top_row; --r)
        result.push_back(cell_id(r, left_col));
    return result;
}

vector<int> trace_spiral(int start, int direction, int turn, const Rect *rect) {
    vector<int> result;
    int current = start;

    while (true) {
        int next = move_to(current, direction);
        bool can_go = is_floor(next) && !used[next] &&
                      (rect == nullptr || inside(*rect, next));

        if (!can_go) {
            int next_direction = (direction + turn + 4) % 4;
            next = move_to(current, next_direction);
            can_go = is_floor(next) && !used[next] &&
                     (rect == nullptr || inside(*rect, next));
            if (!can_go) break;
            direction = next_direction;
        }

        used[next] = 1;
        result.push_back(next);
        current = next;
    }

    return result;
}

bool valid_path(const vector<int> &path) {
    if (static_cast<int>(path.size()) != floor_count) return false;

    fill(used.begin(), used.end(), 0);
    int previous_direction = -1;

    for (int i = 0; i < floor_count; ++i) {
        int p = path[i];
        if (!is_floor(p) || used[p]) return false;
        used[p] = 1;

        if (i > 0) {
            int direction = direction_of(path[i - 1], p);
            if (direction < 0) return false;
            if (previous_direction >= 0 &&
                direction != previous_direction &&
                direction != (previous_direction + 1) % 4) {
                return false;
            }
            previous_direction = direction;
        }
    }

    return true;
}

bool try_boundary_arc(const vector<int> &arc, int order, vector<int> &answer) {
    fill(used.begin(), used.end(), 0);
    for (int p : arc) used[p] = 1;

    int first_direction = direction_of(arc[0], arc[1]);
    int last_direction = direction_of(arc[arc.size() - 2], arc.back());
    vector<int> prefix, suffix;

    if (order == 0) {
        prefix = trace_spiral(arc.front(), (first_direction + 2) % 4, -1, nullptr);
        suffix = trace_spiral(arc.back(), last_direction, +1, nullptr);
    } else {
        suffix = trace_spiral(arc.back(), last_direction, +1, nullptr);
        prefix = trace_spiral(arc.front(), (first_direction + 2) % 4, -1, nullptr);
    }

    if (prefix.size() + arc.size() + suffix.size() !=
        static_cast<size_t>(floor_count)) {
        return false;
    }

    answer.clear();
    for (auto it = prefix.rbegin(); it != prefix.rend(); ++it)
        answer.push_back(*it);
    answer.insert(answer.end(), arc.begin(), arc.end());
    answer.insert(answer.end(), suffix.begin(), suffix.end());
    return valid_path(answer);
}

bool solve_full_perimeter(const vector<int> &perimeter, vector<int> &answer) {
    int perimeter_size = perimeter.size();
    int inner_top = top_row + 1;
    int inner_bottom = bottom_row - 1;
    int inner_left = left_col + 1;
    int inner_right = right_col - 1;

    // perimeter[cut] -> perimeter[cut+1] を使わない外周辺とする。
    for (int cut = 0; cut < perimeter_size; ++cut) {
        int a = perimeter[(cut + 1) % perimeter_size];
        int b = perimeter[cut];
        int ar = row_of(a), ac = col_of(a);
        int br = row_of(b), bc = col_of(b);

        Rect rect_a, rect_b;
        if (ar == br) {
            int split = max(ac, bc);
            Rect left{inner_top, inner_bottom, inner_left, split - 1};
            Rect right{inner_top, inner_bottom, split, inner_right};
            if (ac >= split) {
                rect_a = right;
                rect_b = left;
            } else {
                rect_a = left;
                rect_b = right;
            }
        } else {
            int split = max(ar, br);
            Rect upper{inner_top, split - 1, inner_left, inner_right};
            Rect lower{split, inner_bottom, inner_left, inner_right};
            if (ar >= split) {
                rect_a = lower;
                rect_b = upper;
            } else {
                rect_a = upper;
                rect_b = lower;
            }
        }

        fill(used.begin(), used.end(), 0);
        for (int p : perimeter) used[p] = 1;

        int first_direction = direction_of(a, perimeter[(cut + 2) % perimeter_size]);
        int last_direction = direction_of(
            perimeter[(cut - 1 + perimeter_size) % perimeter_size], b);

        vector<int> prefix = trace_spiral(
            a, (first_direction + 2) % 4, -1, &rect_a);
        vector<int> suffix = trace_spiral(b, last_direction, +1, &rect_b);

        if (prefix.size() + perimeter.size() + suffix.size() !=
            static_cast<size_t>(floor_count)) {
            continue;
        }

        answer.clear();
        for (auto it = prefix.rbegin(); it != prefix.rend(); ++it)
            answer.push_back(*it);
        for (int i = 0; i < perimeter_size; ++i)
            answer.push_back(perimeter[(cut + 1 + i) % perimeter_size]);
        answer.insert(answer.end(), suffix.begin(), suffix.end());

        if (valid_path(answer)) return true;
    }

    return false;
}

void print_answer(const vector<int> &path) {
    int initial_direction = 0;
    if (floor_count >= 2)
        initial_direction = direction_of(path[0], path[1]);

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

    for (int i = 1; i < floor_count; ++i) {
        int next_direction = direction_of(path[i - 1], path[i]);
        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;

    top_row = H;
    bottom_row = -1;
    left_col = W;
    right_col = -1;
    floor_count = 0;

    for (int r = 0; r < H; ++r) {
        for (int c = 0; c < W; ++c) {
            if (grid_data[r][c] == '.') {
                ++floor_count;
                top_row = min(top_row, r);
                bottom_row = max(bottom_row, r);
                left_col = min(left_col, c);
                right_col = max(right_col, c);
            }
        }
    }

    used.assign(H * W, 0);
    vector<int> answer;

    if (top_row == bottom_row) {
        if (floor_count != right_col - left_col + 1) {
            cout << -1 << '\n';
            return 0;
        }
        for (int c = left_col; c <= right_col; ++c)
            answer.push_back(cell_id(top_row, c));
        print_answer(answer);
        return 0;
    }

    if (left_col == right_col) {
        if (floor_count != bottom_row - top_row + 1) {
            cout << -1 << '\n';
            return 0;
        }
        for (int r = top_row; r <= bottom_row; ++r)
            answer.push_back(cell_id(r, left_col));
        print_answer(answer);
        return 0;
    }

    vector<int> perimeter = bounding_perimeter();
    vector<unsigned char> on_perimeter(perimeter.size(), 0);
    int perimeter_floor_count = 0;

    for (int i = 0; i < static_cast<int>(perimeter.size()); ++i) {
        on_perimeter[i] = is_floor(perimeter[i]);
        perimeter_floor_count += on_perimeter[i];
    }

    if (perimeter_floor_count < static_cast<int>(perimeter.size())) {
        int start = -1;
        int intervals = 0;
        for (int i = 0; i < static_cast<int>(perimeter.size()); ++i) {
            int previous = (i - 1 + perimeter.size()) % perimeter.size();
            if (on_perimeter[i] && !on_perimeter[previous]) {
                start = i;
                ++intervals;
            }
        }

        if (intervals != 1) {
            cout << -1 << '\n';
            return 0;
        }

        vector<int> arc;
        for (int i = start; on_perimeter[i]; i = (i + 1) % perimeter.size())
            arc.push_back(perimeter[i]);

        if (arc.size() < 2 ||
            static_cast<int>(arc.size()) != perimeter_floor_count ||
            (!try_boundary_arc(arc, 0, answer) &&
             !try_boundary_arc(arc, 1, answer))) {
            cout << -1 << '\n';
            return 0;
        }

        print_answer(answer);
        return 0;
    }

    if (solve_full_perimeter(perimeter, answer)) {
        print_answer(answer);
    } else {
        cout << -1 << '\n';
    }
    return 0;
}
0