結果

問題 No.5023 Airlines Optimization
コンテスト
ユーザー ぬるぽ
提出日時 2026-02-27 21:53:32
言語 C++23
(gcc 15.2.0 + boost 1.89.0)
コンパイル:
g++-15 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
TLE  
実行時間 -
コード長 24,225 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 4,611 ms
コンパイル使用メモリ 295,552 KB
実行使用メモリ 7,976 KB
スコア 0
最終ジャッジ日時 2026-02-27 21:56:54
合計ジャッジ時間 200,399 ms
ジャッジサーバーID
(参考情報)
judge5 / judge7
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other TLE * 100
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

using namespace std;

struct XorShift64 {
    uint64_t x = 88172645463325252ULL;

    uint64_t next() {
        x ^= x << 7;
        x ^= x >> 9;
        return x;
    }

    uint64_t next(uint64_t mod) {
        if (mod == 0) return 0;
        return next() % mod;
    }

    double next_double() {
        return static_cast<double>(next()) / static_cast<double>(numeric_limits<uint64_t>::max());
    }
};

struct Timer {
    chrono::steady_clock::time_point start = chrono::steady_clock::now();

    double elapsed_sec() const {
        const auto now = chrono::steady_clock::now();
        return chrono::duration<double>(now - start).count();
    }
};

struct Flight {
    int from = -1;
    int to = -1;
    int dep = -1;
    int arr = -1;

    bool operator==(const Flight& other) const = default;
};

struct EvalResult {
    double metric = 0.0;  // approx contest score: 1e6 * share
    unsigned long long win_weight = 0;
    vector<vector<unsigned long long>> loss_pair_weight;
};

struct State {
    vector<vector<Flight>> planes;
    EvalResult eval;
};

class Solver {
public:
    void solve() {
        read_input();
        precompute();

        timer_ = Timer();
        State current = build_initial_state();
        State best = current;

        const double start_temp = 3000.0;
        const double end_temp = 20.0;

        long long iterations = 0;
        long long accepted = 0;

        while (timer_.elapsed_sec() < kTimeLimitSec - 0.01) {
            iterations++;

            State candidate;
            if (!propose_neighbor(current, candidate)) {
                continue;
            }

            candidate.eval = evaluate(candidate.planes);
            const double diff = candidate.eval.metric - current.eval.metric;

            const double progress = min(1.0, timer_.elapsed_sec() / kTimeLimitSec);
            const double temp = start_temp * pow(end_temp / start_temp, progress);

            bool accept = false;
            if (diff >= 0.0) {
                accept = true;
            } else {
                const double prob = exp(diff / max(1e-9, temp));
                accept = (rng_.next_double() < prob);
            }

            if (accept) {
                accepted++;
                current = std::move(candidate);
                if (current.eval.metric > best.eval.metric) {
                    best = current;
                }
            }
        }

        output_state(best);

        cerr << fixed << setprecision(2)
             << "[info] iters=" << iterations
             << " accepted=" << accepted
             << " final=" << current.eval.metric
             << " best=" << best.eval.metric
             << " time=" << timer_.elapsed_sec() << "s\n";
    }

private:
    static constexpr int kStartMin = 6 * 60;
    static constexpr int kEndMin = 21 * 60;
    static constexpr int kNegInf = -1'000'000'000;
    static constexpr double kTimeLimitSec = 1.9;

    int N_ = 0;
    int R_ = 0;
    int M_ = 0;
    int K_ = 0;

    vector<int> xs_;
    vector<int> ys_;
    vector<unsigned long long> ws_;

    vector<Flight> square_flights_;

    vector<vector<double>> dist_;
    vector<vector<int>> dur_;
    vector<vector<char>> eligible_pair_;

    vector<int> target_times_;  // 11:00..21:00, 30-min step
    unsigned long long total_weight_ = 0;

    vector<int> square_latest_;  // flattened [q][dst][src]

    XorShift64 rng_;
    Timer timer_;

    static string strip_bom(string s) {
        if (s.size() >= 3) {
            const unsigned char b0 = static_cast<unsigned char>(s[0]);
            const unsigned char b1 = static_cast<unsigned char>(s[1]);
            const unsigned char b2 = static_cast<unsigned char>(s[2]);
            if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
                return s.substr(3);
            }
        }
        return s;
    }

    static int parse_time(const string& s) {
        // "HH:MM"
        const int hh = stoi(s.substr(0, 2));
        const int mm = stoi(s.substr(3, 2));
        return hh * 60 + mm;
    }

    static string format_time(int minutes) {
        const int hh = minutes / 60;
        const int mm = minutes % 60;
        ostringstream oss;
        oss << setfill('0') << setw(2) << hh << ":" << setw(2) << mm;
        return oss.str();
    }

    int latest_idx(int q, int dst, int src) const {
        return (q * N_ + dst) * N_ + src;
    }

    int calc_flight_duration(int i, int j) const {
        if (i == j) return 0;
        const double dx = static_cast<double>(xs_[i] - xs_[j]);
        const double dy = static_cast<double>(ys_[i] - ys_[j]);
        const double d = sqrt(dx * dx + dy * dy);
        const double raw = 60.0 * d / 800.0 + 40.0;
        const int dur = static_cast<int>(ceil((raw - 1e-12) / 5.0)) * 5;
        return dur;
    }

    void read_input() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);

        string first;
        cin >> first;
        first = strip_bom(first);
        N_ = stoi(first);
        cin >> R_;

        xs_.resize(N_);
        ys_.resize(N_);
        ws_.resize(N_);
        for (int i = 0; i < N_; i++) {
            cin >> xs_[i] >> ys_[i] >> ws_[i];
        }

        cin >> M_;
        square_flights_.reserve(M_);
        for (int i = 0; i < M_; i++) {
            int a, b;
            string s, t;
            cin >> a >> s >> b >> t;
            --a;
            --b;
            square_flights_.push_back(Flight{a, b, parse_time(s), parse_time(t)});
        }

        cin >> K_;
    }

    void precompute() {
        dist_.assign(N_, vector<double>(N_, 0.0));
        dur_.assign(N_, vector<int>(N_, 0));
        eligible_pair_.assign(N_, vector<char>(N_, 0));

        const double threshold = 0.25 * static_cast<double>(R_);

        for (int i = 0; i < N_; i++) {
            for (int j = 0; j < N_; j++) {
                const double dx = static_cast<double>(xs_[i] - xs_[j]);
                const double dy = static_cast<double>(ys_[i] - ys_[j]);
                const double d = sqrt(dx * dx + dy * dy);
                dist_[i][j] = d;
                dur_[i][j] = calc_flight_duration(i, j);
                if (d >= threshold - 1e-12) {
                    eligible_pair_[i][j] = 1;
                }
            }
        }

        target_times_.clear();
        for (int t = 11 * 60; t <= 21 * 60; t += 30) {
            target_times_.push_back(t);
        }

        total_weight_ = 0;
        for (int i = 0; i < N_; i++) {
            for (int j = 0; j < N_; j++) {
                if (!eligible_pair_[i][j]) continue;
                const unsigned long long w = ws_[i] * ws_[j];
                for (int q = 0; q < static_cast<int>(target_times_.size()); q++) {
                    (void)q;
                    total_weight_ += w;
                }
            }
        }

        square_latest_ = compute_latest_departure(square_flights_);
    }

    vector<int> compute_latest_departure(const vector<Flight>& flights) const {
        vector<Flight> sorted = flights;
        sort(sorted.begin(), sorted.end(), [](const Flight& a, const Flight& b) {
            if (a.dep != b.dep) return a.dep > b.dep;
            return a.arr > b.arr;
        });

        const int Q = static_cast<int>(target_times_.size());
        vector<int> latest(Q * N_ * N_, kNegInf);

        vector<int> dp(N_, kNegInf);
        for (int q = 0; q < Q; q++) {
            const int deadline = target_times_[q];
            for (int dst = 0; dst < N_; dst++) {
                fill(dp.begin(), dp.end(), kNegInf);
                dp[dst] = deadline;

                for (const Flight& f : sorted) {
                    if (f.arr <= dp[f.to]) {
                        dp[f.from] = max(dp[f.from], f.dep);
                    }
                }

                for (int src = 0; src < N_; src++) {
                    latest[latest_idx(q, dst, src)] = dp[src];
                }
            }
        }

        return latest;
    }

    EvalResult evaluate(const vector<vector<Flight>>& planes) const {
        vector<Flight> all;
        size_t total_legs = 0;
        for (const auto& plane : planes) total_legs += plane.size();
        all.reserve(total_legs);
        for (const auto& plane : planes) {
            for (const Flight& f : plane) {
                all.push_back(f);
            }
        }

        const vector<int> circle_latest = compute_latest_departure(all);

        EvalResult res;
        res.loss_pair_weight.assign(N_, vector<unsigned long long>(N_, 0));

        unsigned long long win = 0;
        const int Q = static_cast<int>(target_times_.size());

        for (int src = 0; src < N_; src++) {
            for (int dst = 0; dst < N_; dst++) {
                if (!eligible_pair_[src][dst]) continue;

                const unsigned long long w = ws_[src] * ws_[dst];
                for (int q = 0; q < Q; q++) {
                    const int sq = square_latest_[latest_idx(q, dst, src)];
                    const int ci = circle_latest[latest_idx(q, dst, src)];
                    if (sq < ci) {
                        win += w;
                    } else {
                        res.loss_pair_weight[src][dst] += w;
                    }
                }
            }
        }

        res.win_weight = win;
        if (total_weight_ == 0) {
            res.metric = 0.0;
        } else {
            res.metric = static_cast<double>(
                (static_cast<long double>(win) * 1'000'000.0L) /
                static_cast<long double>(total_weight_)
            );
        }
        return res;
    }

    bool validate_plane(const vector<Flight>& plane) const {
        for (int i = 0; i < static_cast<int>(plane.size()); i++) {
            const Flight& f = plane[i];
            if (f.from < 0 || f.from >= N_ || f.to < 0 || f.to >= N_ || f.from == f.to) return false;
            if (f.dep < kStartMin || f.dep > kEndMin || f.arr < kStartMin || f.arr > kEndMin) return false;
            if (f.dep % 5 != 0 || f.arr % 5 != 0) return false;
            if (f.arr - f.dep != dur_[f.from][f.to]) return false;
            if (f.arr < f.dep) return false;

            if (i > 0) {
                const Flight& prev = plane[i - 1];
                if (prev.to != f.from) return false;
                if (prev.arr > f.dep) return false;
            }
        }
        return true;
    }

    int pick_weighted_index(const vector<double>& weights) {
        double sum = 0.0;
        int fallback = -1;
        for (int i = 0; i < static_cast<int>(weights.size()); i++) {
            if (weights[i] > 0.0) {
                sum += weights[i];
                fallback = i;
            }
        }

        if (fallback < 0) {
            return static_cast<int>(rng_.next(weights.size()));
        }

        const double r = rng_.next_double() * sum;
        double acc = 0.0;
        for (int i = 0; i < static_cast<int>(weights.size()); i++) {
            if (weights[i] <= 0.0) continue;
            acc += weights[i];
            if (acc >= r) return i;
        }
        return fallback;
    }

    int choose_start_city(const EvalResult& eval) {
        vector<unsigned long long> row_loss(N_, 0);
        unsigned long long max_row = 1;
        for (int i = 0; i < N_; i++) {
            unsigned long long s = 0;
            for (int j = 0; j < N_; j++) s += eval.loss_pair_weight[i][j];
            row_loss[i] = s;
            max_row = max(max_row, s);
        }

        vector<double> weights(N_, 0.0);
        for (int i = 0; i < N_; i++) {
            const double row_ratio = static_cast<double>(row_loss[i]) / static_cast<double>(max_row);
            weights[i] = static_cast<double>(ws_[i]) * (1.0 + 3.0 * row_ratio);
        }

        return pick_weighted_index(weights);
    }

    struct MoveCandidate {
        bool stop = false;
        int to = -1;
        int dep = -1;
        int arr = -1;
        double weight = 0.0;
    };

    vector<MoveCandidate> make_move_candidates(
        int city,
        int now,
        int depth,
        bool has_target,
        int target_city,
        int target_time,
        bool force_direct,
        const EvalResult& eval,
        unsigned long long max_loss
    ) {
        vector<MoveCandidate> cands;
        const int limit = has_target ? target_time : kEndMin;
        const double threshold = 0.25 * static_cast<double>(R_);

        if (!has_target && depth >= 1) {
            // 非目標区間はある程度伸びたら終了候補も許可
            const double stop_w = 0.3 + 0.1 * min(depth, 6);
            cands.push_back(MoveCandidate{true, -1, -1, -1, stop_w});
        }

        static const int waits[] = {0, 5, 10, 15, 20, 30};

        const int to_begin = force_direct ? target_city : 0;
        const int to_end = force_direct ? target_city + 1 : N_;

        for (int to = to_begin; to < to_end; to++) {
            if (to == city) continue;

            const int d = dur_[city][to];
            if (now + d > limit) continue;

            for (int wait : waits) {
                const int dep = now + wait;
                const int arr = dep + d;
                if (arr > limit) continue;
                if (dep > kEndMin || arr > kEndMin) continue;

                if (has_target) {
                    // 三角不等式 + 固定オーバーヘッドより、直行が最短時間近似として十分強い。
                    if (arr + dur_[to][target_city] > target_time) {
                        continue;
                    }
                }

                const long double pop_score = static_cast<long double>(ws_[city]) * static_cast<long double>(ws_[to]);
                const double loss_ratio = static_cast<double>(eval.loss_pair_weight[city][to]) /
                                          static_cast<double>(max_loss);
                const double loss_score = 1.0 + 5.0 * loss_ratio;
                const double wait_score = 1.0 / (1.0 + static_cast<double>(wait) / 5.0);

                double dist_score = 1.0;
                if (dist_[city][to] >= threshold) {
                    dist_score = threshold / dist_[city][to];
                }

                double target_score = 1.0;
                if (has_target) {
                    const double d_goal = dist_[to][target_city];
                    target_score = 1.0 + threshold / (threshold + d_goal);
                    if (to == target_city) target_score *= 1.8;
                }

                double weight = static_cast<double>(pop_score) * loss_score * wait_score * dist_score * target_score;
                if (weight <= 0.0) weight = 1e-9;

                cands.push_back(MoveCandidate{false, to, dep, arr, weight});
            }
        }

        return cands;
    }

    bool build_route_segment(
        int start_city,
        int start_time,
        bool has_target,
        int target_city,
        int target_time,
        const EvalResult& eval,
        int max_depth,
        int max_nodes,
        vector<Flight>& out
    ) {
        out.clear();
        vector<Flight> path;

        unsigned long long max_loss = 1;
        for (int i = 0; i < N_; i++) {
            for (int j = 0; j < N_; j++) {
                max_loss = max(max_loss, eval.loss_pair_weight[i][j]);
            }
        }

        int visited_nodes = 0;

        function<bool(int, int, int)> dfs = [&](int city, int now, int depth) -> bool {
            const double remain_global = kTimeLimitSec - timer_.elapsed_sec();

            if (remain_global < 0.015) {
                if (has_target) {
                    if (city == target_city && now <= target_time) return true;
                    const int dep = now;
                    const int arr = dep + dur_[city][target_city];
                    if (dep <= kEndMin && arr <= target_time && arr <= kEndMin) {
                        path.push_back(Flight{city, target_city, dep, arr});
                        return true;
                    }
                    return false;
                }
                return true;
            }

            if (has_target) {
                if (city == target_city && now <= target_time) return true;
                if (now > target_time) return false;
                if (now + dur_[city][target_city] > target_time) return false;
            } else {
                if (now >= kEndMin - 5) return true;
                if (depth >= max_depth) return true;
            }

            if (visited_nodes++ >= max_nodes) {
                if (has_target) {
                    return city == target_city && now <= target_time;
                }
                return true;
            }

            bool force_direct = false;
            if (has_target) {
                const int rem = target_time - now;
                if (rem <= dur_[city][target_city] + 10 || remain_global < 0.04) {
                    force_direct = true;
                }
            } else {
                if (remain_global < 0.03) {
                    return true;
                }
            }

            vector<MoveCandidate> cands = make_move_candidates(
                city,
                now,
                depth,
                has_target,
                target_city,
                target_time,
                force_direct,
                eval,
                max_loss
            );

            if (cands.empty()) {
                return !has_target;
            }

            while (!cands.empty()) {
                vector<double> weights;
                weights.reserve(cands.size());
                for (const auto& c : cands) weights.push_back(c.weight);
                const int pick = pick_weighted_index(weights);

                const MoveCandidate cand = cands[pick];
                cands[pick] = cands.back();
                cands.pop_back();

                if (cand.stop) {
                    if (!has_target) return true;
                    continue;
                }

                path.push_back(Flight{city, cand.to, cand.dep, cand.arr});
                if (dfs(cand.to, cand.arr, depth + 1)) {
                    return true;
                }
                path.pop_back();
            }

            return false;
        };

        const bool ok = dfs(start_city, start_time, 0);
        if (ok) {
            out = std::move(path);
        }
        return ok;
    }

    State build_initial_state() {
        State state;
        state.planes.assign(K_, {});

        state.eval = evaluate(state.planes);  // empty schedule baseline

        for (int p = 0; p < K_; p++) {
            vector<Flight> route;
            bool ok = false;

            for (int trial = 0; trial < 8; trial++) {
                const int start_city = choose_start_city(state.eval);
                const int max_depth = 8 + static_cast<int>(rng_.next(8));
                ok = build_route_segment(
                    start_city,
                    kStartMin,
                    false,
                    -1,
                    -1,
                    state.eval,
                    max_depth,
                    1200,
                    route
                );
                if (ok && !route.empty()) break;
            }

            if (!ok || route.empty()) {
                // Fallback: 単発1便
                const int a = choose_start_city(state.eval);
                int b = -1;
                for (int i = 0; i < N_; i++) {
                    if (i == a) continue;
                    if (kStartMin + dur_[a][i] > kEndMin) continue;
                    if (b < 0 || ws_[i] > ws_[b]) b = i;
                }
                if (b < 0) {
                    b = (a + 1) % N_;
                }
                route.push_back(Flight{a, b, kStartMin, kStartMin + dur_[a][b]});
            }

            if (!validate_plane(route)) {
                // 万一の保険
                const int a = 0;
                const int b = 1 % N_;
                route.clear();
                route.push_back(Flight{a, b, kStartMin, kStartMin + dur_[a][b]});
            }

            state.planes[p] = std::move(route);
            state.eval = evaluate(state.planes);
        }

        return state;
    }

    bool propose_neighbor(const State& current, State& out) {
        out = current;

        for (int attempt = 0; attempt < 10; attempt++) {
            const int p = static_cast<int>(rng_.next(K_));
            const vector<Flight>& base = current.planes[p];
            const int m = static_cast<int>(base.size());

            int l = 0;
            int r = m;
            bool keep_suffix = false;

            const int mode_roll = static_cast<int>(rng_.next(100));
            if (m == 0 || mode_roll < 25) {
                // 全破壊
                l = 0;
                r = m;
                keep_suffix = false;
            } else if (mode_roll < 60) {
                // 後半を再構築
                l = static_cast<int>(rng_.next(m + 1));
                r = m;
                keep_suffix = false;
            } else {
                // 中区間を再構築して後続へ接続
                l = static_cast<int>(rng_.next(m + 1));
                if (l == m) l = m - 1;
                int len = 1 + static_cast<int>(rng_.next(max(1, m - l)));
                r = min(m, l + len);
                keep_suffix = (r < m) && (rng_.next(100) < 75);
            }

            vector<Flight> prefix(base.begin(), base.begin() + l);
            vector<Flight> suffix;
            if (keep_suffix) {
                suffix.assign(base.begin() + r, base.end());
            }

            int start_city = -1;
            int start_time = -1;

            if (!prefix.empty()) {
                start_city = prefix.back().to;
                start_time = prefix.back().arr;
            } else {
                const bool reuse_original_start = (!base.empty() && rng_.next(100) < 40);
                start_city = reuse_original_start ? base.front().from : choose_start_city(current.eval);
                start_time = kStartMin;
            }

            bool has_target = false;
            int target_city = -1;
            int target_time = -1;
            if (!suffix.empty()) {
                has_target = true;
                target_city = suffix.front().from;
                target_time = suffix.front().dep;
            }

            vector<Flight> middle;
            const int max_depth = has_target ? 12 : (8 + static_cast<int>(rng_.next(8)));
            const int max_nodes = has_target ? 1600 : 1200;

            const bool ok = build_route_segment(
                start_city,
                start_time,
                has_target,
                target_city,
                target_time,
                current.eval,
                max_depth,
                max_nodes,
                middle
            );
            if (!ok) continue;

            vector<Flight> merged;
            merged.reserve(prefix.size() + middle.size() + suffix.size());
            for (const auto& f : prefix) merged.push_back(f);
            for (const auto& f : middle) merged.push_back(f);
            for (const auto& f : suffix) merged.push_back(f);

            if (merged.empty()) continue;
            if (!validate_plane(merged)) continue;
            if (merged == base) continue;

            out.planes[p] = std::move(merged);
            return true;
        }

        return false;
    }

    void output_state(const State& state) const {
        for (int p = 0; p < K_; p++) {
            const auto& route = state.planes[p];
            cout << route.size() << '\n';
            for (const Flight& f : route) {
                cout << (f.from + 1) << ' '
                     << format_time(f.dep) << ' '
                     << (f.to + 1) << ' '
                     << format_time(f.arr) << '\n';
            }
        }
    }
};

int main() {
    Solver solver;
    solver.solve();
    return 0;
}
0