Submission #1051195


Source Code Expand

#include <algorithm>
#include <array>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
#define U(v) \
cerr << #v << ": " << (v) << endl

class timer {
    int n = 0;
    map<string, timer> ts;
public:
    long long limit = 9700;
    long long t = 0;
    bool elapses() const {
        return time() - t > limit;
    }
    void measure() {
        t = time() - t;
        ++n;
    }
    void measure(const string& id) {
        ts[id].measure();
    }
    void print(const string& id = "TOTAL") {
        if (n % 2 && id == "TOTAL")
            measure();
        for (auto& p : ts)
            p.second.print(p.first);
        fprintf(stderr, "%9s: %4lld, %d\n", id.c_str(), t, n / 2);
    }
    static long long time() {
        return clock() * 1000ll / CLOCKS_PER_SEC;
    }
} timer;

class rando {
    unsigned y = 2463534242;
public:
    unsigned next() {
        return y ^= (y ^= (y ^= y << 13) >> 17) << 5;
    }
    int next(int b) {
        return next() % b;
    }
    int next(int a, int b) {
        return next(b - a) + a;
    }
    double next_double(double b = 1) {
        return b * next() / 4294967296.0;
    }
    double next_double(double a, double b) {
        return next_double(b - a) + a;
    }
    int operator() (int b) {
        return next(b);
    }
} rando;

template <typename Score>
class Annealing {
    const long long START_TIME;
    const double FINAL_HEAT, INITIAL_HEAT, HEAT_COEF;
    Score th;
    double heat;
public:
    Annealing(double initialHeat, double finalHeat, int timeLimit) :
        START_TIME(timer.time()),
        FINAL_HEAT(1 / finalHeat),
        INITIAL_HEAT(1 / initialHeat),
        HEAT_COEF(log(FINAL_HEAT / INITIAL_HEAT) / (timeLimit - START_TIME + timer.t)) {}
    bool cool() {
        heat = INITIAL_HEAT * exp((timer.time() - START_TIME) * HEAT_COEF);
        th = (Score)(7 / heat);
        return heat < FINAL_HEAT;
    }
    bool should_transit(Score diff) const {
        return diff < 1 || diff < th && rando.next_double() < exp(-diff * heat);
    }
};

struct Node {
    int x, y, height, forward = 0, reverse = 0;
    Node* begin, * end, * prev = nullptr, * next = nullptr;
    Node() {}
    Node(int x, int y, int height) : x(x), y(y), height(height) {}
};

const int N = 30;
const int KEEP = 2048;
Node best_sols[KEEP][N][N];
int best_scores[KEEP];

namespace Annealer {

    int diff(Node* n1, Node* n2) {
        return max(0, n1->height - n2->height + 1);
    }

    int evalf(Node* n) {
        return n->end->forward + n->end->height;
    }

    int evalr(Node* n) {
        return n->begin->reverse + n->begin->height;
    }

    int eval(Node* n) {
        return min(evalf(n), evalr(n));
    }

    int eval(Node* n1, Node* n2) {
        return (n1->next ? n1->reverse : n1->forward) +
            diff(n1, n2) + (n2->next ? evalf(n2) : evalr(n2));
    }

    int evalf(Node* n1, Node* n2) {
        return n2->forward + n2->height - n1->forward;
    }

    int evalr(Node* n1, Node* n2) {
        return n2->reverse + n2->height - n1->reverse;
    }

    int evalff(Node* n1, Node* n2) {
        return min(n1->forward + diff(n1, n2) + evalf(n2, n2->end),
            n2->reverse + diff(n2, n1) + evalr(n1, n1->begin));
    }

    int evalfr(Node* n1, Node* n2) {
        return min(n1->forward + diff(n1, n2) + evalr(n2, n2->begin),
            n2->forward + diff(n2, n1) + evalr(n1, n1->begin));
    }

    int evalrf(Node* n1, Node* n2) {
        return min(n1->reverse + diff(n1, n2) + evalf(n2, n2->end),
            n2->reverse + diff(n2, n1) + evalf(n1, n1->end));
    }

    int eval1(Node* n1, Node* n2) {
        return min(evalf(n1, n2), evalr(n2, n1));
    }

    int eval(Node sol[][N]) {
        int score = 0;
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                auto n = &sol[y][x];
                if (n == n->begin)
                    score += eval(n);
            }
        }
        return score;
    }

    int manhattan(Node* n1, Node* n2) {
        return abs(n1->x - n2->x) + abs(n1->y - n2->y);
    }

    vector<Node> cells(Node* n) { 
        vector<Node> cells;
        if (evalf(n) < evalr(n)) {
            for (; n; n = n->next)
                cells.push_back(*n);
        } else {
            for (n = n->end; n; n = n->prev)
                cells.push_back(*n);
        }
        return cells;
    }

    void update(Node* b, Node* e) {
        b->begin = b;
        b->end = e;
        b->prev = nullptr;
        e->next = nullptr;
        b->forward = 0;
        for (auto n = b->next; n; n = n->next) {
            n->begin = b;
            n->end = e;
            n->forward = n->prev->forward + diff(n->prev, n);
        }
        e->reverse = 0;
        for (auto n = e->prev; n; n = n->prev)
            n->reverse = n->next->reverse + diff(n->next, n);
    }

    void updateff(Node* b1, Node* b2, Node* e1, Node* e2) {
        b2->next = e1;
        e1->prev = b2;
        update(b1, e2);
    }

    void updatefr(Node* b1, Node* b2, Node* e1, Node* e2) {
        e2->prev = nullptr;
        for (auto n = e1; n; n = n->next)
            swap(n->next, n->prev);
        updateff(b1, b2, e1, e2);
    }

    void updaterf(Node* b1, Node* b2, Node* e1, Node* e2) {
        b2->prev = nullptr;
        for (auto n = b1; n; n = n->next)
            swap(n->next, n->prev);
        updateff(b1, b2, e1, e2);
    }

    void copy(Node from[][N], Node to[][N]) {
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                auto& f = from[y][x];
                auto& t = to[y][x];
                t.forward = f.forward;
                t.reverse = f.reverse;
                t.begin = &to[f.begin->y][f.begin->x];
                t.end = &to[f.end->y][f.end->x];
                t.prev = f.prev ? &to[f.prev->y][f.prev->x] : nullptr;
                t.next = f.next ? &to[f.next->y][f.next->x] : nullptr;
            }
        }
    }

    Node sol[N][N];
    int score;
    int best_i;

    void transit(int diff) {
        score += diff;
        if (best_scores[best_i] > score) {
            best_scores[best_i] = score;
            copy(sol, best_sols[best_i]);
        }
    }

    void anneal() {
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                int height;
                cin >> height;
                sol[y][x] = { x, y, height };
                sol[y][x].begin = sol[y][x].end = &sol[y][x];
                for (auto& s : best_sols)
                    s[y][x] = Node(x, y, height);
            }
        }
        score = eval(sol);
        fill_n(best_scores, KEEP, score);
        best_i = -1;
        Annealing<int> sa(
            45000,
            0.7,
            7500
        );
        for (int an = -1; ; ) {
            if (!(++an & 0x7ff)) {
                if (!sa.cool()) break;
                best_i = (best_i + 1) % KEEP;
            }

            // select randomly adjacent two cells
            unsigned r = rando.next();
            Node* n1;
            Node* n2;
            {
                int x = (r & 0x7fff) % (N - 1);
                int y = (r >> 17) % N;
                if (r & 0x8000) {
                    n1 = &sol[y][x];
                    n2 = &sol[y][x + 1];
                } else {
                    n1 = &sol[x][y];
                    n2 = &sol[x + 1][y];
                }
            }

            // neighbor1 split one path to two paths
            if (n1->prev == n2 || n1->next == n2) {
                if (n1->prev == n2)
                    swap(n1, n2);
                int curr_score = eval(n1);
                int cand_score = eval1(n1->begin, n1) + eval1(n2, n2->end);
                int diff = cand_score - curr_score;
                if (sa.should_transit(diff)) {
                    update(n1->begin, n1);
                    update(n2, n2->end);
                    transit(diff);
                }

            } else if ((n1->prev && n1->next) != (n2->prev && n2->next)) {
                if (n1->prev && n1->next)
                    swap(n1, n2);

                // neighbor3 split one path includes n2 to two paths. combine splitted one path and one path includes n1
                if (n1->begin != n2->begin) {
                    int curr_score = eval(n1) + eval(n2);
                    int s1 = (n1->next ? n1->reverse : n1->forward) + diff(n1, n2);
                    int s2 = diff(n2, n1) + (n1->next ? evalf(n1) : evalr(n1));
                    if (r & 0x10000) {
                        int cand_score = min(s1 + evalr(n2, n2->begin), n2->forward + s2) + eval1(n2->next, n2->end);
                        int diff = cand_score - curr_score;
                        if (sa.should_transit(diff)) {
                            update(n2->next, n2->end);
                            if (n1->next)
                                updateff(n2->begin, n2, n1, n1->end);
                            else
                                updatefr(n2->begin, n2, n1, n1->begin);
                            transit(diff);
                        }
                    } else {
                        int cand_score = min(s1 + evalf(n2, n2->end), n2->reverse + s2) + eval1(n2->begin, n2->prev);
                        int diff = cand_score - curr_score;
                        if (sa.should_transit(diff)) {
                            update(n2->begin, n2->prev);
                            if (n1->next)
                                updaterf(n1->end, n1, n2, n2->end);
                            else
                                updateff(n1->begin, n1, n2, n2->end);
                            transit(diff);
                        }
                    }

                    // neighbor4 split and combine one path
                } else {
                    int curr_score = eval(n1);
                    if (n1->next) {
                        int cand_score = min(n2->reverse + diff(n2, n1) + evalf(n1, n2->prev),
                            n1->reverse - n2->prev->reverse + diff(n1, n2) + evalf(n2, n2->end));
                        int diff = cand_score - curr_score;
                        if (sa.should_transit(diff)) {
                            updaterf(n2->end, n2, n1, n2->prev);
                            transit(diff);
                        }
                    } else {
                        int cand_score = min(n2->forward + diff(n2, n1) + evalr(n1, n2->next),
                            n1->forward - n2->next->forward + diff(n1, n2) + evalr(n2, n2->begin));
                        int diff = cand_score - curr_score;
                        if (sa.should_transit(diff)) {
                            updatefr(n2->begin, n2, n1, n2->next);
                            transit(diff);
                        }
                    }
                }

                // neighbor5 combine two paths to one path
            } else if (!n1->prev || !n1->next) {
                if (n1->begin == n2->begin) continue;
                int curr_score = eval(n1) + eval(n2);
                int cand_score = min(eval(n1, n2), eval(n2, n1));
                int diff = cand_score - curr_score;
                if (sa.should_transit(diff)) {
                    if (n1->next && n2->next) {
                        updaterf(n1->end, n1, n2, n2->end);
                    } else if (n1->next) {
                        updateff(n2->begin, n2, n1, n1->end);
                    } else if (n2->next) {
                        updateff(n1->begin, n1, n2, n2->end);
                    } else {
                        updatefr(n1->begin, n1, n2, n2->begin);
                    }
                    transit(diff);
                }

                // neighbor2 split and combine two paths
            } else {
                if (n1->begin == n2->begin) continue;
                int curr_score = eval(n1) + eval(n2);
                if (manhattan(n1->prev, n2->prev) == 1) {
                    int cand_score = evalfr(n1->prev, n2->prev) + evalrf(n1, n2);
                    int diff = cand_score - curr_score;
                    if (sa.should_transit(diff)) {
                        updatefr(n1->begin, n1->prev, n2->prev, n2->begin);
                        updaterf(n1->end, n1, n2, n2->end);
                        transit(diff);
                    }
                } else if (manhattan(n1->prev, n2->next) == 1) {
                    int cand_score = evalff(n1->prev, n2->next) + evalff(n2, n1);
                    int diff = cand_score - curr_score;
                    if (sa.should_transit(diff)) {
                        updateff(n1->begin, n1->prev, n2->next, n2->end);
                        updateff(n2->begin, n2, n1, n1->end);
                        transit(diff);
                    }
                } else if (manhattan(n1->next, n2->prev) == 1) {
                    int cand_score = evalff(n2->prev, n1->next) + evalff(n1, n2);
                    int diff = cand_score - curr_score;
                    if (sa.should_transit(diff)) {
                        updateff(n2->begin, n2->prev, n1->next, n1->end);
                        updateff(n1->begin, n1, n2, n2->end);
                        transit(diff);
                    }
                } else if (manhattan(n1->next, n2->next) == 1) {
                    int cand_score = evalrf(n1->next, n2->next) + evalfr(n1, n2);
                    int diff = cand_score - curr_score;
                    if (sa.should_transit(diff)) {
                        updaterf(n1->end, n1->next, n2->next, n2->end);
                        updatefr(n1->begin, n1, n2, n2->begin);
                        transit(diff);
                    }
                }
            }
        }
    }
}

namespace StepConnector {

    using Edge = array<pair<int, int>, 2>;

    vector<vector<Edge>> edges;
    int another_x;
    int another_y;
    int dp[128][128][2];

    int dfs(int x, int y, bool reverse) {
        int& ret = dp[y][x][reverse];
        if (ret >= 0)
            return ret;
        auto& e = edges[y][x][reverse];
        if (e.first >= 0 && (e.first == edges[y][x][!reverse].first || !dfs(e.second, e.first, reverse)))
            return ret = 0;
        int end = y == another_y && x > another_x ? another_x : -1;
        while (--x > end) {
            auto& e = edges[y][x];
            if (e[0].first >= 0 && !dfs(e[0].second, e[0].first, false))
                return ret = 0;
            if (e[1].first >= 0 && !dfs(e[1].second, e[1].first, true))
                return ret = 0;
        }
        return ret = y != another_y || x != another_x;
    }

    bool try_connect(const Edge& c) {
        auto& from = edges[c[0].first][c[0].second][0];
        auto& to = edges[c[1].first][c[1].second][1];
        if (from.first >= 0 || to.first >= 0)
            return false;
        from = c[1];
        to = c[0];
        for (int i = 0; i < 2; ++i) {
            another_x = c[1 - i].second;
            another_y = c[1 - i].first;
            for (int j = 0; j < (int)edges.size(); ++j)
                for (int k = 0; k < (int)edges[j].size(); ++k)
                    dp[j][k][0] = dp[j][k][1] = -1;
            fill_n(dp[another_y][another_x], 2, 0);
            if (!dfs(c[i].second, c[i].first, !i)) {
                from.first = -1;
                to.first = -1;
                return false;
            }
        }
        return true;
    }

    vector<Edge> connect(Node sol[][N], int n, int th) {
        static pair<int, int> step_begin[N][N][100];
        static pair<int, int> step_end[N][N][100];
        memset(step_begin, -1, sizeof step_begin);
        memset(step_end, -1, sizeof step_end);
        int paths_size = 0;
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                auto n = &sol[y][x];
                if (n != n->begin) continue;
                auto cells = Annealer::cells(n);
                int path_size = 0;
                for (int i = 1; ; ) {
                    for (; i < (int)cells.size() && cells[i - 1].height < cells[i].height; ++i);
                    for (int j = i - 1; cells[i - 1].height >= (i == cells.size() ? 1 : cells[i].height); ) {
                        if (cells[j].height) {
                            for (; j && cells[j - 1].height && cells[j].height - cells[j - 1].height == 1; --j);
                        } else {
                            ++j;
                        }
                        for (int k = j; k < i; ++k)
                            --cells[k].height;
                        auto& b = cells[i - 1];
                        auto& e = cells[j];
                        step_begin[b.y][b.x][b.height] = { paths_size, path_size };
                        step_end[e.y][e.x][e.height] = { paths_size, path_size };
                        ++path_size;
                    }
                    if (i == cells.size()) break;
                }
                ++paths_size;
            }
        }
        vector<Edge> cands;
        vector<vector<int>> reindex(paths_size);
        for (int y = 0; y < N; ++y) {
            for (int x = 1; x < N; ++x) {
                for (int height = 1; height < 100; ++height) {
                    {
                        auto& p = step_end[y][x][height];
                        auto& q = step_begin[y][x - 1][height - 1];
                        if (p.first != -1 && q.first != -1 && p.first != q.first) {
                            cands.emplace_back(Edge{ p, q });
                            reindex[p.first].emplace_back(p.second);
                            reindex[q.first].emplace_back(q.second);
                        }
                    } {
                        auto& p = step_end[y][x - 1][height];
                        auto& q = step_begin[y][x][height - 1];
                        if (p.first != -1 && q.first != -1 && p.first != q.first) {
                            cands.emplace_back(Edge{ p, q });
                            reindex[p.first].emplace_back(p.second);
                            reindex[q.first].emplace_back(q.second);
                        }
                    } {
                        auto& p = step_end[x][y][height];
                        auto& q = step_begin[x - 1][y][height - 1];
                        if (p.first != -1 && q.first != -1 && p.first != q.first) {
                            cands.emplace_back(Edge{ p, q });
                            reindex[p.first].emplace_back(p.second);
                            reindex[q.first].emplace_back(q.second);
                        }
                    } {
                        auto& p = step_end[x - 1][y][height];
                        auto& q = step_begin[x][y][height - 1];
                        if (p.first != -1 && q.first != -1 && p.first != q.first) {
                            cands.emplace_back(Edge{ p, q });
                            reindex[p.first].emplace_back(p.second);
                            reindex[q.first].emplace_back(q.second);
                        }
                    }
                }
            }
        }
        if ((int)cands.size() <= th)
            return {};
        for (auto& a : reindex) {
            sort(a.begin(), a.end());
            a.erase(unique(a.begin(), a.end()), a.end());
        }
        for (auto& c : cands) {
            for (auto& p : c) {
                auto& a = reindex[p.first];
                p.second = find(a.begin(), a.end(), p.second) - a.begin();
            }
        }
        edges.resize(paths_size);
        for (int i = 0; i < paths_size; ++i)
            edges[i].assign(reindex[i].size(), {});
        vector<Edge> best_sol;
        for (int i = 0; i < n; ++i) {
            for (auto& es : edges)
                for (auto& e : es)
                    e[0].first = e[1].first = -1;
            random_shuffle(cands.begin(), cands.end(), rando);
            vector<Edge> sol;
            for (auto& c : cands) {
                if (try_connect(c))
                    sol.push_back(c);
            }
            if (best_sol.size() < sol.size())
                best_sol = sol;
        }
        for (auto& e : best_sol)
            for (auto& p : e)
                p.second = reindex[p.first][p.second];
        return best_sol;
    }

    void print(Node sol1[][N], const vector<Edge>& sol2) {
        vector<vector<vector<int>>> paths;
        for (int y = 0; y < N; ++y) {
            for (int x = 0; x < N; ++x) {
                auto n = &sol1[y][x];
                if (n != n->begin) continue;
                auto cells = Annealer::cells(n);
                vector<vector<int>> path;
                for (int i = 1; ; ) {
                    for (; i < (int)cells.size() && cells[i - 1].height < cells[i].height; ++i);
                    for (int j = i - 1; cells[i - 1].height >= (i == cells.size() ? 1 : cells[i].height); ) {
                        if (cells[j].height) {
                            for (; j && cells[j - 1].height && cells[j].height - cells[j - 1].height == 1; --j);
                        } else {
                            ++j;
                        }
                        vector<int> step;
                        for (int k = i - 1; k >= j; --k) {
                            auto& c = cells[k];
                            --c.height;
                            step.emplace_back(c.x);
                            step.emplace_back(c.y);
                        }
                        path.emplace_back(step);
                    }
                    if (i == cells.size()) break;
                }
                paths.emplace_back(path);
            }
        }
        edges.resize(paths.size());
        for (int i = 0; i < (int)edges.size(); ++i)
            edges[i].assign(paths[i].size(), { make_pair(-1, 0), make_pair(-1, 0) });
        for (auto& e : sol2)
            for (int i = 0; i < 2; ++i)
                edges[e[i].first][e[i].second][i] = e[1 - i];
        vector<int> xs(paths.size(), 0);
        while (true) {
            bool any = false;
            for (int y = 0; y < (int)paths.size(); ++y) {
                while (xs[y] < (int)paths[y].size()) {
                    vector<pair<int, int>> ps{ { y, xs[y] } };
                    bool all = true;
                    for (int i = 0; i < 2 && all; ++i) {
                        for (int j = ps.size() - 1; j < (int)ps.size(); ++j) {
                            auto p = edges[ps[j].first][ps[j].second][i];
                            if (p.first >= 0) {
                                if (xs[p.first] < p.second)
                                    all = false;
                                else
                                    ps.emplace_back(p);
                            }
                        }
                        reverse(ps.begin(), ps.end());
                    }
                    if (!all) break;
                    for (auto& p : ps) {
                        auto& step = paths[p.first][p.second];
                        for (int i = 0; i < (int)step.size(); i += 2)
                            cout << step[i + 1] + 1 << ' ' << step[i] + 1 << endl;
                        ++xs[p.first];
                    }
                    any = true;
                }
            }
            if (!any) break;
        }
    }

    void connect() {
        struct Solution {
            int score;
            int i;
            vector<Edge> edges;
            Solution() {}
            Solution(int score, int i, const vector<Edge>& edges) :
                score(score), i(i), edges(edges) {}
            bool operator < (const Solution& sol) const {
                return score < sol.score;
            }
        };
        vector<Solution> sols;
        int best_score = 1 << 30;
        for (int i = 0; i < KEEP; ++i) {
            auto edges = connect(best_sols[i], 2, best_scores[i] - best_score);
            sols.emplace_back(best_scores[i] - edges.size(), i, edges);
            best_score = min(best_score, sols.back().score);
        }
        sort(sols.begin(), sols.end());
        sols.resize(4);
        Solution best{ 1 << 30, 0, {} };
        for (auto& sol : sols) {
            auto edges = connect(best_sols[sol.i], 6, 0);
            if (sol.edges.size() < edges.size())
                sol = Solution(best_scores[sol.i] - edges.size(), sol.i, edges);
            best = min(best, sol);
        }
        print(best_sols[best.i], best.edges);
    }
}

int main() {
    timer.measure();
    Annealer::anneal();
    StepConnector::connect();
    return 0;
}

Submission Info

Submission Time
Task A - 高橋君の山崩しゲーム
User hakomo
Language C++14 (GCC 5.4.1)
Score 899353
Code Size 25355 Byte
Status AC
Exec Time 9169 ms
Memory 104192 KB

Judge Result

Set Name test_01 test_02 test_03 test_04 test_05 test_06 test_07 test_08 test_09 test_10
Score / Max Score 90131 / 100000 89825 / 100000 89865 / 100000 89756 / 100000 89781 / 100000 89901 / 100000 90292 / 100000 89721 / 100000 89868 / 100000 90213 / 100000
Status
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
AC × 1
Set Name Test Cases
test_01 subtask_01_01.txt
test_02 subtask_01_02.txt
test_03 subtask_01_03.txt
test_04 subtask_01_04.txt
test_05 subtask_01_05.txt
test_06 subtask_01_06.txt
test_07 subtask_01_07.txt
test_08 subtask_01_08.txt
test_09 subtask_01_09.txt
test_10 subtask_01_10.txt
Case Name Status Exec Time Memory
subtask_01_01.txt AC 8920 ms 104192 KB
subtask_01_02.txt AC 8871 ms 103936 KB
subtask_01_03.txt AC 8817 ms 103808 KB
subtask_01_04.txt AC 9122 ms 103936 KB
subtask_01_05.txt AC 8841 ms 103936 KB
subtask_01_06.txt AC 9169 ms 103908 KB
subtask_01_07.txt AC 8827 ms 103808 KB
subtask_01_08.txt AC 8924 ms 103936 KB
subtask_01_09.txt AC 8909 ms 103936 KB
subtask_01_10.txt AC 9009 ms 103936 KB