結果
問題 | No.5007 Steiner Space Travel |
ユーザー | tmikada |
提出日時 | 2023-04-30 13:26:42 |
言語 | C++17 (gcc 12.3.0 + boost 1.83.0) |
結果 |
AC
|
実行時間 | 990 ms / 1,000 ms |
コード長 | 11,592 bytes |
コンパイル時間 | 3,700 ms |
コンパイル使用メモリ | 229,304 KB |
実行使用メモリ | 4,496 KB |
スコア | 7,385,470 |
最終ジャッジ日時 | 2023-04-30 13:27:19 |
合計ジャッジ時間 | 36,100 ms |
ジャッジサーバーID (参考情報) |
judge13 / judge12 |
純コード判定しない問題か言語 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 960 ms
4,368 KB |
testcase_01 | AC | 983 ms
4,368 KB |
testcase_02 | AC | 983 ms
4,496 KB |
testcase_03 | AC | 983 ms
4,372 KB |
testcase_04 | AC | 960 ms
4,368 KB |
testcase_05 | AC | 959 ms
4,372 KB |
testcase_06 | AC | 985 ms
4,368 KB |
testcase_07 | AC | 989 ms
4,368 KB |
testcase_08 | AC | 984 ms
4,368 KB |
testcase_09 | AC | 962 ms
4,372 KB |
testcase_10 | AC | 959 ms
4,368 KB |
testcase_11 | AC | 985 ms
4,368 KB |
testcase_12 | AC | 985 ms
4,368 KB |
testcase_13 | AC | 984 ms
4,372 KB |
testcase_14 | AC | 990 ms
4,372 KB |
testcase_15 | AC | 961 ms
4,368 KB |
testcase_16 | AC | 984 ms
4,372 KB |
testcase_17 | AC | 985 ms
4,368 KB |
testcase_18 | AC | 987 ms
4,368 KB |
testcase_19 | AC | 964 ms
4,368 KB |
testcase_20 | AC | 959 ms
4,372 KB |
testcase_21 | AC | 986 ms
4,368 KB |
testcase_22 | AC | 984 ms
4,376 KB |
testcase_23 | AC | 985 ms
4,368 KB |
testcase_24 | AC | 964 ms
4,372 KB |
testcase_25 | AC | 962 ms
4,368 KB |
testcase_26 | AC | 983 ms
4,372 KB |
testcase_27 | AC | 986 ms
4,368 KB |
testcase_28 | AC | 982 ms
4,372 KB |
testcase_29 | AC | 959 ms
4,372 KB |
ソースコード
#include <bits/stdc++.h> #ifdef LOCAL #include <debug_print.hpp> #define debug(...) debug_print::multi_print(#__VA_ARGS__, __VA_ARGS__) #else #define debug(...) (static_cast<void>(0)) #endif using namespace std; using ll=long long; // Start 2023.04.23 21:00 // End // Time min // MTK005 constexpr ll TYPE_PLANET = 1; constexpr ll TYPE_STATION = 2; constexpr ll ALPHA = 5; constexpr ll INF = (1LL << 60); ll START_TIME = 0; struct Pos { int x,y; ll type; Pos(int x, int y, ll type) : x(x), y(y), type(type) {} }; std::ostream& operator<<(std::ostream& os, const Pos& pos) { std::ostringstream oss; oss << "(" << pos.x << ", " << pos.y << "), " << pos.type << ""; os << oss.str(); return os; } struct Solver { int N = 0; int M = 0; vector<Pos> points; Solver(int n, int m, vector<Pos>& points) : N(n),M(m),points(points) {} void test() { debug(N,M,points); } void solve() { // 貪欲法 set_station(); // debug(points); // 全点間エネルギーを計算 vector<vector<int>> distances = calc_distance(); // 惑星1からスタートする // 一番近い惑星+宇宙ステーションを選び続ける vector<int> route = greedy(distances); debug(route); // 宇宙ステーションを1つ選んでずらす { double current_score = calc_score(route); int ti = clock(); int TIMELIMIT = 0.20 * CLOCKS_PER_SEC; int cnt = 1; int kaizen = 0; while(clock() - ti < TIMELIMIT) { cnt++; // ずらす宇宙ステーションをランダムに選ぶ int num = RandInt(N,N+M-1); // debug(num,points.size()); points.erase(points.begin() + num); points.emplace_back(RandInt(1,1000),RandInt(1,1000),TYPE_STATION); } vector<vector<int>> new_distances = calc_distance(); vector<int> new_route = greedy(new_distances); double new_score = calc_score(new_route); if(current_score <= new_score) { kaizen++; current_score = new_score; distances = new_distances; route = new_route; } else { // 元に戻す } debug(cnt,kaizen, kaizen*100/cnt); } // 2-opt { double current_score = calc_score(route); int ti = clock(); // int TIMELIMIT = 0.93 * CLOCKS_PER_SEC; int TIMELIMIT = (0.93-0.20) * CLOCKS_PER_SEC; int cnt = 1; int kaizen = 0; while(clock() - ti < TIMELIMIT) { // for(int t=1; t<400000; t++) { cnt++; int l = RandInt(1,route.size()-2); int r = RandInt(1,route.size()-2); while(l == r) { l = RandInt(1,route.size()-2); r = RandInt(1,route.size()-2); } if(l > r) swap(l,r); if(calc_energy(route[l],route[r]) + calc_energy(route[l+1],route[r+1]) < calc_energy(route[l],route[l+1]) + calc_energy(route[l],route[r+1])) { for(int k=0; k<(r-l)/2; k++) { swap(route[l+1+k],route[r-k]); } } double new_score = calc_score(route); // double T = 30.00 - 28.00 * cnt / 400000.0; // double probability = exp(min(0.0, (current_score - new_score)/T)); // if(current_score <= new_score && Randouble() < probability) { if(current_score <= new_score) { kaizen++; current_score = new_score; } else { // 元に戻す for(int k=0; k<(r-l)/2; k++) { swap(route[l+1+k],route[r-k]); } } } debug(cnt,kaizen, kaizen*100/cnt); } // debug(route); output(route); calc_score(route); } vector<vector<int>> calc_distance() { // 全点間エネルギーを計算 vector<vector<int>> distances(N+M,vector<int>(N+M)); for(int i=0; i<N+M; i++) { for(int j=0; j<N+M; j++) { distances[i][j] = calc_energy(i,j); } } // debug(distances); // ワーシャルフロイド // kを経由した方がエネルギーが少ない場合もある for(int k=0; k<N+M; k++) { for(int i=0; i<N+M; i++) { for(int j=0; j<N+M; j++) { int d = distances[i][k] + distances[k][j]; distances[i][j] = min(distances[i][j],d); } } } return distances; } vector<int> greedy(vector<vector<int>>& distances) { // 惑星1からスタートする // 一番近い惑星+宇宙ステーションを選び続ける vector<int> visited(N+M,0); vector<int> route; visited[0]++; route.push_back(0); int v = 0; for(int i=0; i<N-1; i++) { int nearest_dist = INT_MAX; int nearest_v = -1; // 一番近い惑星を探す for(int next=0; next<N; next++) { if(visited[next] > 0) continue; // int d = calc_energy(v,next); int d = distances[v][next]; if(d < nearest_dist) { nearest_dist = d; nearest_v = next; } // debug(d); } // 次の頂点に移動 // dijkstraで経路を復元 vector<int> path = djikstra(v,nearest_v); for(auto p : path) { route.push_back(p); } v = nearest_v; visited[v]++; // route.push_back(nearest_v); } // 最後に惑星1に戻る route.push_back(0); // debug(visited); debug(route); return route; } void output(vector<int>& route) { // 宇宙ステーションの座標 for(int i=N; i<N+M; i++) { if(points[i].type == TYPE_STATION) { cout << points[i].x << " " << points[i].y << endl; } } // 経路の長さ cout << route.size() << endl; // 経路 for(auto i : route) { Pos pos = points[i]; if(pos.type == TYPE_PLANET) { cout << pos.type << " " << i+1 << endl; } else { cout << pos.type << " " << i+1-N << endl; } } } void set_station() { // 宇宙ステーションの座標は適当に決め打ち vector<pair<int,int>> v; v.push_back({300,300}); v.push_back({300,500}); v.push_back({300,700}); v.push_back({500,300}); v.push_back({500,700}); v.push_back({700,300}); v.push_back({700,500}); v.push_back({700,700}); for(int i=0; i<M; i++) { // points.emplace_back(Pos(v[i].first,v[i].second,TYPE_STATION)); points.emplace_back(v[i].first,v[i].second,TYPE_STATION); // points.emplace_back(RandInt(0,1000),RandInt(0,1000),TYPE_STATION); } } void TwoOpt(vector<int>& route, vector<vector<int>>& distances) { debug(route.size()); for(int i=1; i<route.size()-3; i++) { for(int j=i+2; j<route.size()-1; j++) { // debug(i,j); // if(distances[route[i]][route[j]] + distances[route[i+1]][route[j+1]] // < distances[route[i]][route[i+1]] + distances[route[j]][route[j+1]]) { if(calc_energy(route[i],route[j]) + calc_energy(route[i+1],route[j+1]) < calc_energy(route[i],route[i+1]) + calc_energy(route[j],route[j+1])) { for(int k=0; k<(j-i)/2; k++) { // int temp = route[i+1+k]; // route[i+1+k] = route[j-k]; // route[j-k] = temp; debug(route[i+1+k],route[j-k]); swap(route[i+1+k],route[j-k]); } } } } } double calc_score(vector<int>& route) { ll s = 0; for(int i=0; i<route.size()-1; i++) { s += calc_energy(route[i],route[i+1]); } // debug(s); double score = round(1000000000/(1000+sqrt(s))); // debug(score); return score; } // a以上b以下のランダム int RandInt(int a, int b) { return a + rand() % (b-a+1); } // 0以上1以下のランダムな実数を返す double Randouble() { return 1.0 * rand() / RAND_MAX; } int calc_energy(int i, int j) { int dx, dy; int energy; auto [x1, y1, type1] = points[i]; auto [x2, y2, type2] = points[j]; dx = x1 - x2; dy = y1 - y2; energy = dx * dx + dy * dy; // 惑星かどうか判定 if (type1 == TYPE_PLANET) { energy *= 5; } if (type2 == TYPE_PLANET) { energy *= 5; } return energy; } vector<int> djikstra(int i, int j) { // 二点間の最短距離 // vector<vector<ll>> dij(N+M); // 経路復元用 1つ前にいた頂点を保存 vector<int> prev_points(N+M,-1); // int k = 0; // for(int k=0; k<N+M; k++) { vector<ll> cur(N+M,INF); vector<bool> kakutei(N+M+1,false); priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> Q; int start = i; cur[start] = 0; Q.push(make_pair(cur[start],start)); while(!Q.empty()) { auto[d, pos] = Q.top(); Q.pop(); if(kakutei[pos] == true) continue; kakutei[pos] = true; for(int next=0; next<N+M; next++) { int cost = calc_energy(pos,next); if(cur[next] > cur[pos]+cost) { // 1つ前の点を保存 prev_points[next] = pos; // 値を更新 cur[next] = cur[pos]+cost; Q.push(make_pair(cur[next],next)); } } } // dij[i] = cur; // } // debug(dij); // debug(cur); // debug(prev_points); // 経路復元 // ゴールからたどっていく int v = j; vector<int> path; while(v != i) { path.push_back(v); v = prev_points[v]; } reverse(path.begin(),path.end()); return path; } }; int main() { START_TIME = clock(); int n,m; cin >> n >> m; // vector<int> a(n),b(n); vector<Pos> points; for(int i=0; i<n; i++) { int a,b; cin >> a >> b; points.emplace_back(Pos(a,b,TYPE_PLANET)); } debug(n,m,points); Solver solver(n,m,points); solver.solve(); return 0; }