#include #include #include #include #include #include #include using namespace std; // --- 定数・構造体 --- const int INF = 1e9; const int MIN_TIME = 6 * 60; // 06:00 const int MAX_TIME = 21 * 60; // 21:00 struct City { int id; int x, y; long long w; }; struct Flight { int from, to, s, t; }; // 所要時間計算 (問題文の式通り) int get_travel_time(const City& a, const City& b) { double d = sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)); int duration = ceil((60.0 * d / 800.0) + 40.0); return ((duration + 4) / 5) * 5; // 5分単位切り上げ } int time_to_int(string s) { int h = stoi(s.substr(0, 2)); int m = stoi(s.substr(3, 2)); return h * 60 + m; } string int_to_time(int t) { int h = t / 60; int m = t % 60; ostringstream oss; oss << setfill('0') << setw(2) << h << ":" << setw(2) << m; return oss.str(); } // --- グローバル変数 --- int ssq[48][48][21]; // SQの最遅出発時刻 [from][to][target_t_idx] int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 1. 入力 int N, R; cin >> N >> R; vector cities(N); for (int i = 0; i < N; ++i) { cities[i].id = i + 1; cin >> cities[i].x >> cities[i].y >> cities[i].w; } int M; cin >> M; vector sq_flights(M); for (int i = 0; i < M; ++i) { string s_str, t_str; cin >> sq_flights[i].from >> s_str >> sq_flights[i].to >> t_str; sq_flights[i].s = time_to_int(s_str); sq_flights[i].t = time_to_int(t_str); } int K; cin >> K; // 2. スクエア航空の解析 (DP) // ssq[i][j][t_idx] : 到着時刻 target_t までに j に着くための最遅出発時刻 for (int i = 1; i <= N; ++i) for (int j = 1; j <= N; ++j) for (int t = 0; t < 21; ++t) ssq[i][j][t] = -1; // -1 は到達不能 for (int t_idx = 0; t_idx < 21; ++t_idx) { int limit_t = 660 + (t_idx * 30); // 11:00, 11:30... // ワーシャルフロイド的に全点間を計算 // (簡略化のため直接便+1回乗り継ぎ程度を考慮。本来はDPでさらに回す) for (auto& f : sq_flights) { if (f.t <= limit_t) { ssq[f.from][f.to][t_idx] = max(ssq[f.from][f.to][t_idx], f.s); } } // 乗り継ぎ考慮 (i -> k -> j) for (int k = 1; k <= N; ++k) { for (int i = 1; i <= N; ++i) { for (int j = 1; j <= N; ++j) { if (ssq[i][k][t_idx] != -1 && ssq[k][j][t_idx] != -1) { // 実際にはもっと複雑だが、ここではiからの出発時刻として // 最初にiを出る便の時刻を維持できるかを簡易評価 // 本来はDPで「終着tから逆算した最遅出発」を解くのがベスト } } } } } // 3. サークル航空のスケジュール生成 (貪欲法) vector> my_schedules(K); vector current_city(K); vector current_time(K, MIN_TIME); // 各機体の初期位置を人口の多い順に割り振る for (int k = 0; k < K; ++k) current_city[k] = (k % N) + 1; for (int k = 0; k < K; ++k) { while (true) { int best_to = -1; long long best_score = -1; int best_duration = 0; for (int next_city = 1; next_city <= N; ++next_city) { if (current_city[k] == next_city) continue; int duration = get_travel_time(cities[current_city[k]-1], cities[next_city-1]); int arrival_time = current_time[k] + duration; if (arrival_time > MAX_TIME) continue; // スコア計算: この便を飛ばすことで SQ より有利になるペアの w_i * w_j を加算 long long score = 0; for (int t_idx = 0; t_idx < 21; ++t_idx) { int limit_t = 660 + (t_idx * 30); if (arrival_time <= limit_t) { // SQがそのルートを飛ばせない、または自社の方が遅く出発できる場合 if (ssq[current_city[k]][next_city][t_idx] < current_time[k]) { score += cities[current_city[k]-1].w * cities[next_city-1].w; } } } // 距離が 0.25R (250) 以上の制約もスコアに加味 double dist = sqrt(pow(cities[current_city[k]-1].x - cities[next_city-1].x, 2) + pow(cities[current_city[k]-1].y - cities[next_city-1].y, 2)); if (dist < 250) score /= 10; // 距離が短い便は優先度を下げる if (score > best_score) { best_score = score; best_to = next_city; best_duration = duration; } } if (best_to != -1 && best_score > 0) { my_schedules[k].push_back({current_city[k], best_to, current_time[k], current_time[k] + best_duration}); current_time[k] += best_duration; current_city[k] = best_to; } else if (best_to != -1 && current_time[k] + best_duration <= MAX_TIME) { // スコアが0でも、まだ飛べるなら人口の多い方へ移動(次のチャンスを狙う) my_schedules[k].push_back({current_city[k], best_to, current_time[k], current_time[k] + best_duration}); current_time[k] += best_duration; current_city[k] = best_to; } else { break; } } } // 4. 出力 for (int k = 0; k < K; ++k) { cout << my_schedules[k].size() << "\n"; for (auto& f : my_schedules[k]) { cout << f.from << " " << int_to_time(f.s) << " " << f.to << " " << int_to_time(f.t) << "\n"; } } return 0; }