結果

問題 No.5007 Steiner Space Travel
ユーザー t33ft33f
提出日時 2022-07-30 18:17:01
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 982 ms / 1,000 ms
コード長 11,768 bytes
コンパイル時間 1,639 ms
実行使用メモリ 6,952 KB
スコア 8,725,939
最終ジャッジ日時 2022-07-30 18:17:45
合計ジャッジ時間 33,926 ms
ジャッジサーバーID
(参考情報)
judge14 / judge10
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 981 ms
5,156 KB
testcase_01 AC 982 ms
5,156 KB
testcase_02 AC 982 ms
5,160 KB
testcase_03 AC 982 ms
5,164 KB
testcase_04 AC 982 ms
4,900 KB
testcase_05 AC 982 ms
4,904 KB
testcase_06 AC 981 ms
5,160 KB
testcase_07 AC 982 ms
4,904 KB
testcase_08 AC 982 ms
4,904 KB
testcase_09 AC 982 ms
4,904 KB
testcase_10 AC 981 ms
6,948 KB
testcase_11 AC 981 ms
4,904 KB
testcase_12 AC 982 ms
4,904 KB
testcase_13 AC 982 ms
6,948 KB
testcase_14 AC 982 ms
4,900 KB
testcase_15 AC 982 ms
4,904 KB
testcase_16 AC 982 ms
4,904 KB
testcase_17 AC 981 ms
4,900 KB
testcase_18 AC 982 ms
6,952 KB
testcase_19 AC 982 ms
4,900 KB
testcase_20 AC 982 ms
4,904 KB
testcase_21 AC 982 ms
6,948 KB
testcase_22 AC 981 ms
6,952 KB
testcase_23 AC 981 ms
6,952 KB
testcase_24 AC 982 ms
4,900 KB
testcase_25 AC 982 ms
6,948 KB
testcase_26 AC 982 ms
4,908 KB
testcase_27 AC 982 ms
6,952 KB
testcase_28 AC 981 ms
4,900 KB
testcase_29 AC 982 ms
6,952 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <cmath>
#include <map>
#include <array>
#include <algorithm>
#include <chrono>
#include <random>
#include <vector>
#include <numeric>
#include <cassert>
#include <iostream>
using namespace std;

unsigned int xor128() {
    static unsigned int x=123456789, y=362436069, z=521288629, w=88675123;
    unsigned int t;
    t=(x^(x<<11)); x=y; y=z; z=w;
    return (w=(w^(w>>19))^(t^(t>>8)));
}

inline bool rand_bool(double prob) {
    constexpr double x = 1LL<<32; // uint_max+1
    return xor128() < prob * x;
}


class Timer {
    chrono::system_clock::time_point start_time = chrono::system_clock::now();
public:
    Timer() {}
    long long get_elapsed_time() {
        auto diff = chrono::system_clock::now() - start_time;
        return chrono::duration_cast<chrono::milliseconds>(diff).count();
    }
} timer;

using P = pair<int, int>;
constexpr int N = 100, M = 8;

int a[N], b[N];

mt19937 mt;

constexpr int alpha = 5;

int L1(P p, P q) { return abs(p.first - q.first) + abs(p.second - q.second); }

int L22(int x1, int y1, int x2, int y2) {
    const int dx = x1 - x2, dy = y1 - y2;
    return dx * dx + dy * dy;
}

int L22(P p, P q) { return L22(p.first, p.second, q.first, q.second); }

int cost(int i, int j) {
    return L22(a[i], b[i], a[j], b[j]) * alpha * alpha;
}

int cost(int i, int j, P middle) {
    return (L22(a[i], b[i], middle.first, middle.second) +
            L22(a[j], b[j], middle.first, middle.second)) * alpha;
}

int cost(int t1, P p1, int t2, P p2) {
    const int coeff = (t1 == 1 && t2 == 1 ? alpha * alpha :
                       t1 == 1 || t2 == 1 ? alpha :
                       1);
    return coeff * L22(p1, p2);
}

array<array<int, N>, N> mincost;
array<array<int, N>, N> next_idx;
void calc_mincost() {
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++) {
            mincost[i][j] = cost(i, j);
            next_idx[i][j] = j;
        }
    for (int k = 0; k < N; k++)
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
                if (mincost[i][k] + mincost[k][j] < mincost[i][j]) {
                    mincost[i][j] = mincost[i][k] + mincost[k][j];
                    assert(i != k);
                    assert(j != k);
                    next_idx[i][j] = next_idx[i][k];
                }
}

void read_input() {
    {
        int n, m;
        cin >> n >> m;
        assert(n == N);
        assert(m == M);
    }
    for (int i = 0; i < N; i++) cin >> a[i] >> b[i];

    calc_mincost();
}

vector<int> solve_tsp(int tl) {
    vector<int> ans(N);
    iota(ans.begin(), ans.end(), 0);
    int total_cost = 0;
    for (int i = 0; i < N; i++) {
        if (i + 1 < N) total_cost += mincost[i][i + 1];
        else total_cost += mincost[0][N - 1];
    }

    Timer tsp_timer;
    uniform_int_distribution<int> randindex(0, N - 1);
    int iter = 0, last_upd = 0, stop_threshold = N * N;
    while (tsp_timer.get_elapsed_time() < tl && last_upd + stop_threshold > iter) {
        int i = randindex(mt), j = randindex(mt);
        assert(i < N);
        assert(j < N);
        if (i == j) continue;
        if (i > j) swap(i, j);
        if (i + 1 == j) continue;

        iter++;
        const int k = (j + 1 == N ? 0 : j + 1);
        // const int diff = (cost(ans[i], ans[j]) + cost(ans[i + 1], ans[k])
        //                   - cost(ans[i], ans[i + 1]) - cost(ans[j], ans[k]));
        const int diff = (mincost[ans[i]][ans[j]] + mincost[ans[i + 1]][ans[k]]
                          - mincost[ans[i]][ans[i + 1]] - mincost[ans[j]][ans[k]]);
        if (diff <= 0) {
            reverse(ans.begin() + i + 1, ans.begin() + j + 1);
            total_cost += diff;
            last_upd = iter;
            // cerr << iter << ' ' << total_cost << endl;
        }
    }

    cerr << iter << ' ' << total_cost << endl;
    return ans;
}

class DSU {
    vector<int> par, rank;
public:
    explicit DSU(int n) : par(n), rank(n, 0) {
        iota(par.begin(), par.end(), 0);
    }

    int find(int i) {
        return (par[i] == i) ? i : (par[i] = find(par[i]));
    }

    void merge(int i, int j) {
        int x = find(i), y = find(j);
        if (x != y) {
            if (rank[x] < rank[y])
                par[x] = y;
            else {
                par[y] = x;
                if (rank[x] == rank[y]) rank[x]++;
            }
        }
    }

    bool same(int i, int j) { return find(i) == find(j); }
};

struct Target {
    int type, idx;
    Target(int type, int idx) : type(type), idx(idx) {
        assert(type == 1 || type == 2);
    }
    bool operator!=(const Target &rhs) {
        return type != rhs.type || idx != rhs.idx;
    }
};

struct Config {
    vector<int> path;
    array<P, M> stations;
};

pair<vector<Target>, long long> greedy(const Config &conf) {
    vector<Target> ans;
    array<bool, N> visited;
    fill(visited.begin(), visited.end(), false);
    ans.emplace_back(1, conf.path[0]);
    visited[conf.path[0]] = true;
    long long total_cost = 0;
    for (int i = 0; i < N; i++) {
        const int from = ans.back().idx, to = conf.path[i + 1];
        if (visited[to]) continue;
        int cur_cost = mincost[from][to], s = -1;
        for (int j = 0; j < M; j++) {
            const int tmp = cost(from, to, conf.stations[j]);
            if (tmp < cur_cost) {
                cur_cost = tmp;
                s = j;
            }
        }
        if (s >= 0) {
            const auto [t1, i1] = ans.back();
            assert(t1 == 1);
            total_cost += cost(1, make_pair(a[i1], b[i1]), 2, conf.stations[s]);
            ans.emplace_back(2, s);
            total_cost += cost(2, conf.stations[s], 1, make_pair(a[to], b[to]));
            ans.emplace_back(1, to);
            visited[to] = true;
        } else {
            for (int cur = from; cur != to; cur = next_idx[cur][to]) {
                const auto [t1, i1] = ans.back();
                assert(t1 == 1);
                assert(i1 == cur);
                const int nxt = next_idx[cur][to];
                total_cost += cost(1, make_pair(a[cur], b[cur]), 1, make_pair(a[nxt], b[nxt]));
                ans.emplace_back(1, nxt);
                visited[nxt] = true;
            }
        }
    }
    if (ans.back() != Target{1, 0}) ans.emplace_back(1, 0);

    return {ans, total_cost};
}

int timelimit = 1000, margin = 20;

struct Solution {
    array<P, M> stations;
    vector<Target> moves;
};

Solution improve(const Solution &sol, int tl) {
    Timer timer;
    array<array<long long, M + N>, M + N> dist;
    array<array<int, M + N>, M + N> next_idx;
    for (int i = 0; i < N + M; i++)
        for (int j = 0; j < N + M; j++) {
            if (i < N) {
                if (j < N) {
                    dist[i][j] = mincost[i][j];
                } else {
                    const P p1(a[i], b[i]), p2 = sol.stations[j - N];
                    dist[i][j] = cost(1, p1, 2, p2);
                }
            } else {
                if (j < N) {
                    const P p1 = sol.stations[i - N], p2(a[j], b[j]);
                    dist[i][j] = cost(2, p1, 1, p2);
                } else {
                    const P p1 = sol.stations[i - N], p2 = sol.stations[j - N];
                    dist[i][j] = cost(2, p1, 2, p2);
                }
            }
            next_idx[i][j] = j;
        }
    for (int k = 0; k < N + M; k++) {
        for (int i = 0; i < N + M; i++) {
            for (int j = 0; j < N + M; j++) {
                const long long tmp = dist[i][k] + dist[k][j];
                if (tmp < dist[i][j]) {
                    dist[i][j] = tmp;
                    next_idx[i][j] = next_idx[i][k];
                }
            }
        }
    }

    vector<int> path;
    vector<bool> visited(N, false);
    for (auto [t, i] : sol.moves) {
        if (t == 1 && !visited[i]) {
            path.push_back(i);
            visited[i] = true;
        }
    }
    path.push_back(0);

    const int n = int(path.size());
    uniform_int_distribution<int> randindex(0, n - 2);
    while (timer.get_elapsed_time() < tl) {
        int i = randindex(mt), j = randindex(mt);
        assert(i < n);
        assert(j < n);
        if (i == j) continue;
        if (i > j) swap(i, j);
        if (i + 1 == j) continue;

        // iter++;
        const int k = j + 1;
        const int diff = (dist[path[i]][path[j]] + dist[path[i + 1]][path[k]]
                          - dist[path[i]][path[i + 1]] - dist[path[j]][path[k]]);
        if (diff <= 0) {
            reverse(path.begin() + i + 1, path.begin() + j + 1);
            // total_cost += diff;
            // last_upd = iter;
            // cerr << iter << ' ' << total_cost << endl;
        }
    }

    Solution newsol;
    newsol.stations = sol.stations;
    newsol.moves.emplace_back(1, 0);
    for (size_t i = 0; i + 1 < path.size(); i++) {
        const int from = path[i], to = path[i + 1];
        for (int cur = from; cur != to; cur = next_idx[cur][to]) {
            const int nxt = next_idx[cur][to];
            if (nxt < N) newsol.moves.emplace_back(1, nxt);
            else newsol.moves.emplace_back(2, nxt - N);
        }
    }
    return newsol;
}

int main() {
    read_input();
    vector<int> path = solve_tsp(200);
    {
        auto start = find(path.begin(), path.end(), 0);
        assert(start != path.end());
        rotate(path.begin(), start, path.end());
        assert(path.front() == 0);
        path.push_back(0);
    }

    array<P, M> stations{
        P{200, 333}, {200, 667},
        {400, 333}, {400, 667},
        {600, 333}, {600, 667},
        {800, 333}, {800, 667},
    };

    auto [cur_ans, cur_cost] = greedy({path, stations});
    Solution best_ans = {stations, cur_ans};
    long long best_cost = cur_cost;
    int iter = 0;
    uniform_int_distribution<int> rand_pos(10, 990);
    uniform_int_distribution<int> rand_diff(-20, 20);
    while (timer.get_elapsed_time() + margin < 900) {
        iter++;
        const int i = iter % M;
        const P origp = stations[i],
            newp = [&] {
                if (timer.get_elapsed_time() < timelimit / 2) {
                    return P(rand_pos(mt), rand_pos(mt));
                } else {
                    const int dx = rand_diff(mt), dy = rand_diff(mt);
                    return P(clamp(origp.first + dx, 10, 990),
                             clamp(origp.second + dy, 10, 990));
                }
            }();
        stations[i] = newp;
        const auto [tmp_ans, tmp_cost] = greedy({path, stations});
        if (tmp_cost < best_cost) {
            cerr << iter << ' ' << tmp_cost << ' ' << round(1e9 / (1000 + sqrt(tmp_cost))) << endl;
            best_cost = tmp_cost;
            best_ans = {stations, tmp_ans};
            // for (auto [x, y] : best_ans.stations) cout << x << ' ' << y << '\n';
        }

        const long long diff = tmp_cost - cur_cost;
        bool accept = diff <= 0;
        if (!accept) {
            const double start_temp = 1e4, end_temp = 1,
                progress = double(timer.get_elapsed_time()) / timelimit,
                temp = start_temp + (end_temp - start_temp) * progress,
                prob = exp(-diff / temp);
            accept = rand_bool(prob);
            // cerr << diff << ' ' << prob << endl;
        }
        if (accept) {
            cur_cost = tmp_cost;
            cur_ans = tmp_ans;
        } else {
            // rollback
            stations[i] = origp;
        }
    }

    cerr << iter << ' ' << round(1e9 / (1000 + sqrt(best_cost))) << endl;

    best_ans = improve(best_ans, 100);
    // output
    for (auto [x, y] : best_ans.stations) cout << x << ' ' << y << '\n';
    cout << best_ans.moves.size() << '\n';
    for (auto [t, i] : best_ans.moves) cout << t << ' ' << i + 1 << '\n';
}
0