#include #include #include #include #include #include #include #include #include #include #include // --- 定数 --- const int N_const = 47; const int R_const = 1000; const int M_const = 400; const int K_const = 25; const int MIN_TIME = 360; // 06:00 const int MAX_TIME = 1260; // 21:00 // --- 構造体 --- struct City { int id; int x, y; long long w; }; struct Flight { int from, to; int start_time, end_time; }; // --- グローバル変数 --- std::vector cities; std::vector square_flights; std::vector> dists; std::vector> flight_times; std::vector> target_pairs; std::vector>> s_sq_table; std::vector hubs; std::vector> gap_adj; std::vector is_hub_vec; // 乱数生成器 std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); // 時間管理 auto start_time = std::chrono::steady_clock::now(); // --- 時間変換ユーティリティ --- int time_to_int(const std::string& t) { return std::stoi(t.substr(0, 2)) * 60 + std::stoi(t.substr(3, 2)); } std::string int_to_time(int t) { std::ostringstream oss; oss << std::setw(2) << std::setfill('0') << t / 60 << ":" << std::setw(2) << std::setfill('0') << t % 60; return oss.str(); } // --- 計算ユーティリティ --- void precompute_distances_and_times() { dists.assign(N_const, std::vector(N_const)); flight_times.assign(N_const, std::vector(N_const)); for (int i = 0; i < N_const; ++i) { for (int j = 0; j < N_const; ++j) { double dx = cities[i].x - cities[j].x; double dy = cities[i].y - cities[j].y; dists[i][j] = std::sqrt(dx * dx + dy * dy); if (i == j) { flight_times[i][j] = 0; } else { flight_times[i][j] = std::ceil((60.0 * dists[i][j] / 800.0 + 40.0) / 5.0) * 5; } } } } // --- スコア計算関連 --- std::vector calculate_latest_departures(int dest_city, int target_arrival, const std::vector& flights) { // This function is a bottleneck. Using adjacency list for incoming flights // and a priority queue for Dijkstra's will speed it up. thread_local static std::vector> incoming_flights(N_const); for(int i = 0; i < N_const; ++i) incoming_flights[i].clear(); for(const auto& f : flights) { incoming_flights[f.to].push_back(&f); } std::vector reachable_at(N_const, -1); std::priority_queue> pq; reachable_at[dest_city] = target_arrival; pq.push({target_arrival, dest_city}); while(!pq.empty()){ auto [time, u] = pq.top(); pq.pop(); if(time < reachable_at[u]) { continue; } for(const Flight* f_ptr : incoming_flights[u]){ const Flight& f = *f_ptr; if (f.end_time <= time) { if (reachable_at[f.from] < f.start_time) { reachable_at[f.from] = f.start_time; pq.push({f.start_time, f.from}); } } } } std::vector latest_dep = reachable_at; for (int i = 0; i < N_const; ++i) { for (int k = 0; k < N_const; ++k) { if (reachable_at[k] != -1) { int dep_time = reachable_at[k] - flight_times[i][k]; if (dep_time >= MIN_TIME) { if (latest_dep[i] < dep_time) { latest_dep[i] = dep_time; } } } } } return latest_dep; } void precompute_sq_table() { s_sq_table.assign(N_const, std::vector>(21, std::vector(N_const))); for (int j = 0; j < N_const; ++j) { for (int t_idx = 0; t_idx < 21; ++t_idx) { int target_arrival = 11 * 60 + t_idx * 30; s_sq_table[j][t_idx] = calculate_latest_departures(j, target_arrival, square_flights); } } } double calculate_score(const std::vector>& solution) { std::vector all_circle_flights; for (const auto& plane_schedule : solution) { all_circle_flights.insert(all_circle_flights.end(), plane_schedule.begin(), plane_schedule.end()); } long double v_ci = 0; long double v_sq = 0; for (const auto& p : target_pairs) { int i = p.first; int j = p.second; long double potential = (long double)cities[i].w * cities[j].w; std::vector s_ci_for_dest; for (int t_idx = 0; t_idx < 21; ++t_idx) { int target_arrival = 11 * 60 + t_idx * 30; if (t_idx == 0 || j != p.second) { // Re-calculate only if dest changes s_ci_for_dest = calculate_latest_departures(j, target_arrival, all_circle_flights); } int s_ci = s_ci_for_dest[i]; int s_sq = s_sq_table[j][t_idx][i]; if (s_ci > s_sq) { v_ci += potential; } else { v_sq += potential; } } } if (v_ci + v_sq == 0) return 0.0; return (double)(v_ci / (v_ci + v_sq)); } // --- 探索アルゴリズム --- std::vector> generate_initial_solution() { std::vector> solution(K_const); for (int i = 0; i < K_const; ++i) { if (hubs.empty()) break; // No hubs, no point. int start_hub = hubs[i % hubs.size()]; int current_city = start_hub; int current_time = MIN_TIME; while (true) { int next_city = -1; bool currently_at_hub = is_hub_vec[current_city]; if (currently_at_hub) { // At a hub, greedily fly to an underserved non-hub city (spoke) std::vector possible_dests; if (!gap_adj[current_city].empty()) { for (int dest : gap_adj[current_city]) { if (!is_hub_vec[dest]) { // Prefer non-hub "gap" routes possible_dests.push_back(dest); } } } if (!possible_dests.empty()) { next_city = possible_dests[rng() % possible_dests.size()]; } else { // Fallback: fly to a random non-hub city if (N_const > hubs.size()) { do { next_city = rng() % N_const; } while (is_hub_vec[next_city]); } else { // All cities are hubs next_city = rng() % N_const; } } } else { // At a spoke, fly back to a hub next_city = hubs[rng() % hubs.size()]; } if (next_city == -1 || next_city == current_city) { if (N_const > 1) next_city = (current_city + 1) % N_const; else break; // Cannot move } if (next_city == current_city) break; int travel_t = flight_times[current_city][next_city]; int end_t = current_time + travel_t; if (end_t > MAX_TIME) break; solution[i].push_back({current_city, next_city, current_time, end_t}); current_city = next_city; current_time = end_t; } } return solution; } std::vector> get_neighbor(const std::vector>& current_solution) { auto new_solution = current_solution; int plane_idx = std::uniform_int_distribution(0, K_const - 1)(rng); if (new_solution[plane_idx].empty()) { return new_solution; // Cannot modify empty schedule } int flight_idx = std::uniform_int_distribution(0, new_solution[plane_idx].size() - 1)(rng); int original_from = new_solution[plane_idx][flight_idx].from; int new_to; double r = std::uniform_real_distribution(0.0, 1.0)(rng); if (r < 0.5 && !hubs.empty()) { // Strategy 1: Fly to a hub new_to = hubs[rng() % hubs.size()]; } else if (r < 0.7 && !gap_adj[original_from].empty()) { // Strategy 2: Serve a gap route from the current location new_to = gap_adj[original_from][rng() % gap_adj[original_from].size()]; } else { // Fallback: random city to maintain diversity new_to = rng() % N_const; } if (new_to == original_from) { // Ensure not flying to the same city new_to = (original_from + 1) % N_const; } // Apply change and propagate int current_city = original_from; int current_time = (flight_idx == 0) ? MIN_TIME : new_solution[plane_idx][flight_idx - 1].end_time; for (int i = flight_idx; i < new_solution[plane_idx].size(); ++i) { int from_city = current_city; // The first modified flight goes to our heuristically chosen `new_to` int to_city = (i == flight_idx) ? new_to : new_solution[plane_idx][i].to; int start_t = current_time; int travel_t = flight_times[from_city][to_city]; int end_t = start_t + travel_t; if (end_t > MAX_TIME) { // Invalid move return current_solution; // Return original, move is rejected } new_solution[plane_idx][i] = {from_city, to_city, start_t, end_t}; current_city = to_city; current_time = end_t; } return new_solution; } void solve() { precompute_distances_and_times(); for (int i = 0; i < N_const; ++i) { for (int j = 0; j < N_const; ++j) { if (i != j && dists[i][j] >= 0.25 * R_const) { target_pairs.push_back({i, j}); } } } precompute_sq_table(); // --- Heuristic Strategy Setup --- // 1. Hub Airport Identification const int NUM_HUBS = 10; std::vector city_indices(N_const); std::iota(city_indices.begin(), city_indices.end(), 0); std::sort(city_indices.begin(), city_indices.end(), [&](int a, int b){ return cities[a].w > cities[b].w; }); if(N_const > 0) { hubs.assign(city_indices.begin(), city_indices.begin() + std::min((int)city_indices.size(), NUM_HUBS)); } is_hub_vec.assign(N_const, false); for(int hub_city : hubs) { is_hub_vec[hub_city] = true; } // 2. Underserved "Gap" Route Identification gap_adj.assign(N_const, std::vector()); const int GAP_THRESHOLD = 5; for (int i = 0; i < N_const; ++i) { for (int j = 0; j < N_const; ++j) { if (i == j) continue; int served_count = 0; for (int t_idx = 0; t_idx < 21; ++t_idx) { if (s_sq_table[j][t_idx][i] != -1) { served_count++; } } if (served_count < GAP_THRESHOLD) { gap_adj[i].push_back(j); } } } // --- End of Heuristic Strategy Setup --- auto current_solution = generate_initial_solution(); double current_score = calculate_score(current_solution); auto best_solution = current_solution; double best_score = current_score; int iter = 0; const double T_start = 1e-4, T_end = 1e-7; const double time_limit_sec = 0.95; while (true) { auto now = std::chrono::steady_clock::now(); double elapsed_sec = std::chrono::duration(now - start_time).count(); if (elapsed_sec > time_limit_sec) break; iter++; auto new_solution = get_neighbor(current_solution); double new_score = calculate_score(new_solution); double temp = T_start + (T_end - T_start) * elapsed_sec / time_limit_sec; if (new_score > current_score || std::uniform_real_distribution(0.0, 1.0)(rng) < std::exp((new_score - current_score) / temp)) { current_solution = new_solution; current_score = new_score; } if (current_score > best_score) { best_solution = current_solution; best_score = current_score; } } // Output the best solution for (int i = 0; i < K_const; ++i) { std::cout << best_solution[i].size() << std::endl; for (const auto& f : best_solution[i]) { std::cout << f.from + 1 << " " << int_to_time(f.start_time) << " " << f.to + 1 << " " << int_to_time(f.end_time) << std::endl; } } } int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); int N_in, R_in; std::cin >> N_in >> R_in; cities.resize(N_const); for (int i = 0; i < N_const; ++i) { cities[i].id = i; std::cin >> cities[i].x >> cities[i].y >> cities[i].w; } int M_in; std::cin >> M_in; square_flights.resize(M_const); for (int i = 0; i < M_const; ++i) { int a, b; std::string s, t; std::cin >> a >> s >> b >> t; square_flights[i] = {a - 1, b - 1, time_to_int(s), time_to_int(t)}; } int K_in; std::cin >> K_in; solve(); return 0; }