結果

問題 No.5023 Airlines Optimization
コンテスト
ユーザー hitonanode
提出日時 2026-02-25 23:59:52
言語 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  
実行時間 -
コード長 18,570 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 3,756 ms
コンパイル使用メモリ 275,500 KB
実行使用メモリ 7,968 KB
スコア 0
最終ジャッジ日時 2026-02-26 00:04:23
合計ジャッジ時間 124,197 ms
ジャッジサーバーID
(参考情報)
judge3 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other TLE * 100
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <algorithm>
#include <chrono>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <iostream>
#include <limits>
#include <numeric>
#include <random>
#include <string>
#include <vector>

namespace {

constexpr int START_TIME = 6 * 60;
constexpr int END_TIME = 21 * 60;
constexpr int SCORE_START = 11 * 60;
constexpr int SCORE_INTERVAL = 30;
constexpr int DEADLINE_COUNT = 21;
constexpr int SOURCE_CAND_LIMIT = 46;
constexpr uint32_t RNG_SEED = 20260225u;
constexpr int DESTROY_MIN_PLANES = 2;
constexpr int DESTROY_MAX_PLANES = 4;
constexpr double REPAIR_TIME_LIMIT_SEC = 0.90;
constexpr int NEG_INF = -1'000'000'000;

struct City {
    int x = 0;
    int y = 0;
    long long w = 0;
};

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

struct Candidate {
    bool valid = false;
    long long gain = std::numeric_limits<long long>::min();
    int from = -1;
    int to = -1;
    int dep = -1;
    int arr = -1;
};

struct PlaneBuildResult {
    std::vector<Flight> route;
    long long gain = 0;
};

int parse_time(const std::string& s) {
    return std::stoi(s.substr(0, 2)) * 60 + std::stoi(s.substr(3, 2));
}

std::string format_time(int t) {
    char buf[6];
    std::snprintf(buf, sizeof(buf), "%02d:%02d", t / 60, t % 60);
    return std::string(buf);
}

int idx3(int n, int i, int j, int k) {
    return (i * n + j) * DEADLINE_COUNT + k;
}

bool is_better_candidate(const Candidate& a, const Candidate& b) {
    if (!b.valid) {
        return true;
    }
    const int da = a.arr - a.dep;
    const int db = b.arr - b.dep;
    const __int128 lhs = static_cast<__int128>(a.gain) * static_cast<__int128>(db);
    const __int128 rhs = static_cast<__int128>(b.gain) * static_cast<__int128>(da);
    if (lhs != rhs) {
        return lhs > rhs;
    }
    if (a.gain != b.gain) {
        return a.gain > b.gain;
    }
    if (a.dep != b.dep) {
        return a.dep > b.dep;
    }
    if (a.arr != b.arr) {
        return a.arr > b.arr;
    }
    if (a.to != b.to) {
        return a.to < b.to;
    }
    return a.from < b.from;
}

bool parse_bool_token(const std::string& s, bool& out) {
    std::string v = s;
    std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) {
        return static_cast<char>(std::tolower(c));
    });
    if (v == "1" || v == "on" || v == "true") {
        out = true;
        return true;
    }
    if (v == "0" || v == "off" || v == "false") {
        out = false;
        return true;
    }
    return false;
}

long long estimate_score_like_objective(
    int n,
    const std::vector<std::vector<long long>>& weight,
    const std::vector<int>& sq_latest,
    const std::vector<int>& ci_latest
) {
    long long v_ci = 0;
    long long v_sq = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            const long long w = weight[i][j];
            if (w == 0) {
                continue;
            }
            for (int k = 0; k < DEADLINE_COUNT; ++k) {
                const int p = idx3(n, i, j, k);
                if (ci_latest[p] > sq_latest[p]) {
                    v_ci += w;
                } else {
                    v_sq += w;
                }
            }
        }
    }
    if (v_ci + v_sq == 0) {
        return 0;
    }
    return static_cast<long long>((1'000'000.0L * static_cast<long double>(v_ci)) /
                                  static_cast<long double>(v_ci + v_sq));
}

bool flight_desc_order(const Flight& a, const Flight& b) {
    if (a.dep != b.dep) {
        return a.dep > b.dep;
    }
    if (a.arr != b.arr) {
        return a.arr > b.arr;
    }
    if (a.to != b.to) {
        return a.to < b.to;
    }
    return a.from < b.from;
}

std::vector<int> compute_ci_latest_exact(
    int n,
    const std::vector<Flight>& flights_sorted,
    const std::vector<int>& deadlines
) {
    std::vector<int> ci_latest(n * n * DEADLINE_COUNT, NEG_INF);
    std::vector<int> latest(n, NEG_INF);
    for (int dest = 0; dest < n; ++dest) {
        for (int d = 0; d < DEADLINE_COUNT; ++d) {
            std::fill(latest.begin(), latest.end(), NEG_INF);
            latest[dest] = deadlines[d];
            for (const Flight& f : flights_sorted) {
                if (latest[f.to] >= f.arr && latest[f.from] < f.dep) {
                    latest[f.from] = f.dep;
                }
            }
            for (int src = 0; src < n; ++src) {
                ci_latest[idx3(n, src, dest, d)] = latest[src];
            }
        }
    }
    return ci_latest;
}

std::vector<int> compute_latest_to_target_deadline(
    int n,
    const std::vector<Flight>& flights_sorted,
    int target,
    int deadline
) {
    std::vector<int> latest(n, NEG_INF);
    latest[target] = deadline;
    for (const Flight& f : flights_sorted) {
        if (latest[f.to] >= f.arr && latest[f.from] < f.dep) {
            latest[f.from] = f.dep;
        }
    }
    return latest;
}

PlaneBuildResult build_plane_greedy(
    bool snap_arrival_to_30,
    const std::vector<std::vector<int>>& nearest_sources,
    const std::vector<std::vector<int>>& duration,
    const std::vector<std::vector<long long>>& weight,
    const std::vector<int>& sq_latest,
    const std::vector<int>& deadlines,
    std::vector<Flight>& flights_sorted,
    std::vector<int>& ci_latest
) {
    const int n = static_cast<int>(duration.size());
    PlaneBuildResult result;
    std::vector<Flight> rev_route;
    long long plane_gain = 0;

    auto evaluate_candidate_gain = [&](int from, int to, int dep, int arr) {
        if (dep < START_TIME || arr > END_TIME) {
            return 0LL;
        }

        std::vector<std::pair<int, int>> downstream_states;
        downstream_states.reserve(n * DEADLINE_COUNT);
        for (int dest = 0; dest < n; ++dest) {
            bool used = false;
            for (int src = 0; src < n; ++src) {
                if (weight[src][dest] > 0) {
                    used = true;
                    break;
                }
            }
            if (!used) {
                continue;
            }
            for (int k = 0; k < DEADLINE_COUNT; ++k) {
                if (ci_latest[idx3(n, to, dest, k)] >= arr) {
                    downstream_states.push_back({dest, k});
                }
            }
        }
        if (downstream_states.empty()) {
            return 0LL;
        }

        const std::vector<int> latest_to_from =
            compute_latest_to_target_deadline(n, flights_sorted, from, dep);

        long long gain = 0;
        for (int src = 0; src < n; ++src) {
            const int candidate_depart = latest_to_from[src];
            if (candidate_depart <= NEG_INF / 2) {
                continue;
            }
            for (const auto& [dest, k] : downstream_states) {
                const int p = idx3(n, src, dest, k);
                if (candidate_depart > sq_latest[p] && candidate_depart > ci_latest[p]) {
                    gain += weight[src][dest];
                }
            }
        }
        return gain;
    };

    auto push_flight_and_recompute = [&](const Flight& f) {
        const auto it = std::lower_bound(flights_sorted.begin(), flights_sorted.end(), f, flight_desc_order);
        flights_sorted.insert(it, f);
        ci_latest = compute_ci_latest_exact(n, flights_sorted, deadlines);
    };

    Candidate best_init;
    const int init_arrival = END_TIME;
    for (int terminal = 0; terminal < n; ++terminal) {
        for (const int from : nearest_sources[terminal]) {
            const int dep = init_arrival - duration[from][terminal];
            if (dep < START_TIME) {
                continue;
            }
            Candidate cand;
            cand.valid = true;
            cand.from = from;
            cand.to = terminal;
            cand.dep = dep;
            cand.arr = init_arrival;
            cand.gain = evaluate_candidate_gain(cand.from, cand.to, cand.dep, cand.arr);
            if (is_better_candidate(cand, best_init)) {
                best_init = cand;
            }
        }
    }

    if (!best_init.valid || best_init.gain <= 0) {
        return result;
    }

    const Flight init_flight{best_init.from, best_init.to, best_init.dep, best_init.arr};
    push_flight_and_recompute(init_flight);
    rev_route.push_back(init_flight);
    plane_gain += best_init.gain;

    int head_city = best_init.from;
    int head_departure = best_init.dep;

    while (true) {
        if (head_departure < START_TIME) {
            break;
        }
        Candidate best_next;
        int fixed_arrival = head_departure;
        if (snap_arrival_to_30) {
            fixed_arrival = ((head_departure + 15) / 30) * 30;
            fixed_arrival = std::min(fixed_arrival, END_TIME);
            if (fixed_arrival > head_departure) {
                fixed_arrival -= 30;
            }
        }
        if (fixed_arrival < START_TIME) {
            break;
        }

        for (const int from : nearest_sources[head_city]) {
            const int dep = fixed_arrival - duration[from][head_city];
            if (dep < START_TIME) {
                continue;
            }
            Candidate cand;
            cand.valid = true;
            cand.from = from;
            cand.to = head_city;
            cand.dep = dep;
            cand.arr = fixed_arrival;
            cand.gain = evaluate_candidate_gain(cand.from, cand.to, cand.dep, cand.arr);
            if (is_better_candidate(cand, best_next)) {
                best_next = cand;
            }
        }

        if (!best_next.valid || best_next.gain <= 0) {
            break;
        }

        const Flight next_flight{best_next.from, head_city, best_next.dep, best_next.arr};
        push_flight_and_recompute(next_flight);
        rev_route.push_back(next_flight);
        plane_gain += best_next.gain;

        head_city = best_next.from;
        head_departure = best_next.dep;
    }

    std::reverse(rev_route.begin(), rev_route.end());
    result.route = std::move(rev_route);
    result.gain = plane_gain;
    return result;
}

}  // namespace

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

    bool snap_arrival_to_30 = false;
    if (const char* env = std::getenv("ARRIVAL_30"); env != nullptr) {
        bool parsed = false;
        if (!parse_bool_token(env, parsed)) {
            std::cerr << "invalid ARRIVAL_30: " << env << "\n";
            return 1;
        }
        snap_arrival_to_30 = parsed;
    }

    int n = 0;
    int r = 0;
    std::cin >> n >> r;

    std::vector<City> cities(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> cities[i].x >> cities[i].y >> cities[i].w;
    }

    int m = 0;
    std::cin >> m;
    std::vector<Flight> sq_flights(m);
    for (int i = 0; i < m; ++i) {
        std::string s;
        std::string t;
        std::cin >> sq_flights[i].from >> s >> sq_flights[i].to >> t;
        --sq_flights[i].from;
        --sq_flights[i].to;
        sq_flights[i].dep = parse_time(s);
        sq_flights[i].arr = parse_time(t);
    }

    int k_plane = 0;
    std::cin >> k_plane;

    std::vector<int> deadlines(DEADLINE_COUNT);
    for (int k = 0; k < DEADLINE_COUNT; ++k) {
        deadlines[k] = SCORE_START + SCORE_INTERVAL * k;
    }

    std::vector<std::vector<int>> duration(n, std::vector<int>(n, 0));
    std::vector<std::vector<long long>> weight(n, std::vector<long long>(n, 0));
    std::vector<std::vector<int>> nearest_sources(n);
    const long double threshold = static_cast<long double>(r) * 0.25L;

    for (int dest = 0; dest < n; ++dest) {
        std::vector<std::pair<long long, int>> cand;
        cand.reserve(n - 1);
        for (int src = 0; src < n; ++src) {
            if (src == dest) {
                continue;
            }
            const long long dx = static_cast<long long>(cities[src].x) - cities[dest].x;
            const long long dy = static_cast<long long>(cities[src].y) - cities[dest].y;
            const long long d2 = dx * dx + dy * dy;
            cand.push_back({d2, src});
        }
        std::sort(cand.begin(), cand.end(), [](const auto& a, const auto& b) {
            if (a.first != b.first) {
                return a.first < b.first;
            }
            return a.second < b.second;
        });
        const int lim = std::min(SOURCE_CAND_LIMIT, static_cast<int>(cand.size()));
        nearest_sources[dest].reserve(lim);
        for (int i = 0; i < lim; ++i) {
            nearest_sources[dest].push_back(cand[i].second);
        }
    }

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (i == j) {
                continue;
            }
            const long double dx = static_cast<long double>(cities[i].x - cities[j].x);
            const long double dy = static_cast<long double>(cities[i].y - cities[j].y);
            const long double dist = std::sqrt(dx * dx + dy * dy);
            const long double raw = 60.0L * dist / 800.0L + 40.0L;
            duration[i][j] = static_cast<int>(std::ceil((raw - 1e-12L) / 5.0L)) * 5;

            if (dist + 1e-12L >= threshold) {
                weight[i][j] = cities[i].w * cities[j].w;
            }
        }
    }

    std::sort(sq_flights.begin(), sq_flights.end(), [](const Flight& a, const Flight& b) {
        if (a.dep != b.dep) {
            return a.dep > b.dep;
        }
        if (a.arr != b.arr) {
            return a.arr > b.arr;
        }
        if (a.to != b.to) {
            return a.to < b.to;
        }
        return a.from < b.from;
    });

    std::vector<int> sq_latest(n * n * DEADLINE_COUNT, NEG_INF);
    std::vector<int> latest(n, NEG_INF);
    for (int dest = 0; dest < n; ++dest) {
        for (int d = 0; d < DEADLINE_COUNT; ++d) {
            std::fill(latest.begin(), latest.end(), NEG_INF);
            latest[dest] = deadlines[d];
            for (const Flight& f : sq_flights) {
                if (latest[f.to] >= f.arr && latest[f.from] < f.dep) {
                    latest[f.from] = f.dep;
                }
            }
            for (int src = 0; src < n; ++src) {
                sq_latest[idx3(n, src, dest, d)] = latest[src];
            }
        }
    }

    std::vector<Flight> current_flights_sorted;
    std::vector<int> ci_latest = compute_ci_latest_exact(n, current_flights_sorted, deadlines);
    std::vector<std::vector<Flight>> answer(k_plane);

    std::cerr << "arrival30=" << (snap_arrival_to_30 ? "on" : "off") << "\n";

    // 初期解: 全機体を通常貪欲で構築
    for (int plane_id = 0; plane_id < k_plane; ++plane_id) {
        PlaneBuildResult built = build_plane_greedy(
            snap_arrival_to_30,
            nearest_sources,
            duration,
            weight,
            sq_latest,
            deadlines,
            current_flights_sorted,
            ci_latest
        );
        answer[plane_id] = std::move(built.route);
        std::cerr << "plane " << (plane_id + 1) << ": flights=" << answer[plane_id].size()
                  << ", gain=" << built.gain
                  << ", approx_score=" << estimate_score_like_objective(n, weight, sq_latest, ci_latest)
                  << "\n";
    }

    long long current_score = estimate_score_like_objective(n, weight, sq_latest, ci_latest);
    std::cerr << "initial_score=" << current_score << "\n";

    // 破壊再構築: ランダムに 2〜4 機を白紙に戻して再貪欲
    std::mt19937 rng(RNG_SEED);
    std::uniform_int_distribution<int> destroy_cnt_dist(DESTROY_MIN_PLANES, DESTROY_MAX_PLANES);
    std::vector<int> perm(k_plane);
    std::iota(perm.begin(), perm.end(), 0);

    int iterations = 0;
    int accepted = 0;
    const auto repair_start = std::chrono::steady_clock::now();
    while (true) {
        const auto now = std::chrono::steady_clock::now();
        const double elapsed = std::chrono::duration<double>(now - repair_start).count();
        if (elapsed >= REPAIR_TIME_LIMIT_SEC) {
            break;
        }
        ++iterations;

        std::shuffle(perm.begin(), perm.end(), rng);
        const int destroy_cnt = std::min(k_plane, destroy_cnt_dist(rng));
        std::vector<int> destroyed_planes(perm.begin(), perm.begin() + destroy_cnt);
        std::vector<char> destroyed(k_plane, 0);
        for (int p : destroyed_planes) {
            destroyed[p] = 1;
        }

        std::vector<Flight> trial_flights;
        for (int plane_id = 0; plane_id < k_plane; ++plane_id) {
            if (destroyed[plane_id]) {
                continue;
            }
            for (const Flight& f : answer[plane_id]) {
                trial_flights.push_back(f);
            }
        }
        std::sort(trial_flights.begin(), trial_flights.end(), flight_desc_order);
        std::vector<int> trial_ci_latest = compute_ci_latest_exact(n, trial_flights, deadlines);

        std::vector<std::vector<Flight>> rebuilt_routes(destroy_cnt);
        for (int i = 0; i < destroy_cnt; ++i) {
            PlaneBuildResult rebuilt = build_plane_greedy(
                snap_arrival_to_30,
                nearest_sources,
                duration,
                weight,
                sq_latest,
                deadlines,
                trial_flights,
                trial_ci_latest
            );
            rebuilt_routes[i] = std::move(rebuilt.route);
        }

        const long long trial_score = estimate_score_like_objective(n, weight, sq_latest, trial_ci_latest);
        if (trial_score > current_score) {
            ++accepted;
            for (int i = 0; i < destroy_cnt; ++i) {
                answer[destroyed_planes[i]] = std::move(rebuilt_routes[i]);
            }
            current_flights_sorted = std::move(trial_flights);
            ci_latest.swap(trial_ci_latest);
            current_score = trial_score;
            std::cerr << "improve iter=" << iterations
                      << " score=" << current_score
                      << " destroy=" << destroy_cnt
                      << " accepted=" << accepted << "\n";
        }
    }

    std::cerr << "repair_done iter=" << iterations
              << " accepted=" << accepted
              << " final_score=" << current_score << "\n";

    for (int plane_id = 0; plane_id < k_plane; ++plane_id) {
        const auto& route = answer[plane_id];
        std::cout << route.size() << "\n";
        for (const Flight& f : route) {
            std::cout << (f.from + 1) << ' ' << format_time(f.dep) << ' '
                      << (f.to + 1) << ' ' << format_time(f.arr) << "\n";
        }
    }

    return 0;
}
0