Submission #75823137


Source Code Expand

/**
 * @file AHC065_chokudai_v3.cpp
 * @brief AHC065 A - Conveyor Design : multi-loop, slack search, rollout.
 * @details
 *   v3 over AHC065_chokudai_v2.cpp
 *   --------------------------------------------------------------
 *   v2 ranked candidate delivery paths by a *static* proxy: the sum of
 *   exit-distances of the next K boxes (disruptionSum). That snapshot
 *   ignores that delivering box k+1 itself scrambles box k+2, etc.
 *
 *   v3 replaces the proxy for the final choice with a real ROLLOUT.
 *   For each of the top NCAND candidate paths of the current box, it
 *   greedily delivers the next ROLLOUT_R boxes on a scratch grid and
 *   counts the actual operations used. The candidate minimising
 *
 *       (ops to deliver box k) + (ops of the k+1..k+R rollout)
 *
 *   is chosen. Everything is measured in real operations, so no WTURN
 *   unit-matching hack is needed for the final decision (WTURN still
 *   guides the intermediate Chokudai beam, which keeps the cheap proxy).
 *
 *   Topology note: the racetrack move graph equals the full grid graph,
 *   so per-box op-distance is already the Manhattan lower bound. The
 *   only remaining lever is operation sharing across deliveries, which
 *   is exactly what the rollout optimises.
 *
 *   Tunable via env vars: AHC_K, AHC_ITERS, AHC_SLACK, AHC_WTURN,
 *                         AHC_R, AHC_NCAND.
 */

#include <bits/stdc++.h>
using namespace std;
using P = pair<int, int>;

const int N = 20, EXR = 0, EXC = 10;
const double TIME_LIMIT = 1.85;

// Tunable parameters (overridable via environment variables for auto-tuning).
// Defaults set by Optuna + pahcer tuning (50 seeds): avg score ~5.92M.
int K_DISRUPT = 36;           // upcoming boxes feeding the intermediate proxy
int ITERS = 111;              // Chokudai iterations per box delivery
int SLACK = 5;                // max extra operations allowed per delivery
int WTURN = 48;               // op weight for the intermediate proxy score
int ROLLOUT_R = 3;            // boxes greedily rolled out to rank candidates
int NCAND = 48;               // candidate paths re-ranked by rollout

int envInt(const char *name, int def) {
    const char *v = getenv(name);
    return v ? atoi(v) : def;
}

int grid[20][20];                         // current box number, -1 if empty
vector<vector<P>> belts;                  // 20 belts, each 40 cells
int hbelt[20][20], hidx[20][20];          // H-loop id / index for each cell
int vbelt[20][20], vidx[20][20];          // V-loop id / index for each cell
int distm[20][20];                        // BFS op-distance to the exit
int curBox;                               // box currently being delivered

// --- belt layout -----------------------------------------------------------
void buildBelts() {
    belts.assign(20, {});
    for (int k = 0; k < 10; ++k) {                 // horizontal loops 0..9
        int r0 = 2 * k, r1 = 2 * k + 1;
        for (int c = 0; c < 20; ++c) belts[k].push_back({r0, c});
        for (int c = 19; c >= 0; --c) belts[k].push_back({r1, c});
    }
    for (int k = 0; k < 10; ++k) {                 // vertical loops 10..19
        int m = 10 + k, c0 = 2 * k, c1 = 2 * k + 1;
        for (int r = 0; r < 20; ++r) belts[m].push_back({r, c0});
        for (int r = 19; r >= 0; --r) belts[m].push_back({r, c1});
    }
    for (int m = 0; m < 20; ++m)
        for (int t = 0; t < 40; ++t) {
            int r = belts[m][t].first, c = belts[m][t].second;
            if (m < 10) { hbelt[r][c] = m; hidx[r][c] = t; }
            else        { vbelt[r][c] = m; vidx[r][c] = t; }
        }
}

// The four cells reachable from (r,c) by rotating its H/V loop by +-1.
inline void neighbors(int r, int c, int nm[4], int nd[4], int nr[4], int nc[4]) {
    int mh = hbelt[r][c], ih = hidx[r][c];
    int mv = vbelt[r][c], iv = vidx[r][c];
    int bm[4] = {mh, mh, mv, mv};
    int bd[4] = {1, -1, 1, -1};
    int bi[4] = {(ih + 1) % 40, (ih + 39) % 40, (iv + 1) % 40, (iv + 39) % 40};
    for (int e = 0; e < 4; ++e) {
        nm[e] = bm[e]; nd[e] = bd[e];
        nr[e] = belts[bm[e]][bi[e]].first;
        nc[e] = belts[bm[e]][bi[e]].second;
    }
}

// --- BFS distance from the exit (move graph is undirected) -----------------
void buildDist() {
    for (int i = 0; i < 20; ++i)
        for (int j = 0; j < 20; ++j) distm[i][j] = -1;
    queue<P> q;
    distm[EXR][EXC] = 0;
    q.push({EXR, EXC});
    while (!q.empty()) {
        auto [r, c] = q.front(); q.pop();
        int nm[4], nd[4], nr[4], nc[4];
        neighbors(r, c, nm, nd, nr, nc);
        for (int e = 0; e < 4; ++e)
            if (distm[nr[e]][nc[e]] == -1) {
                distm[nr[e]][nc[e]] = distm[r][c] + 1;
                q.push({nr[e], nc[e]});
            }
    }
}

// rotate belt m by direction d on an arbitrary grid
void rotateG(int g[20][20], int m, int d) {
    const auto &b = belts[m];
    int L = 40, old[40];
    for (int x = 0; x < L; ++x) old[x] = g[b[x].first][b[x].second];
    for (int x = 0; x < L; ++x) {
        int y = (x + d + L) % L;
        g[b[y].first][b[y].second] = old[x];
    }
}

// --- Chokudai search state -------------------------------------------------
struct State {
    int g[20][20];
    int tr, tc;                      // current cell of the delivered box
    vector<pair<int, int>> ops;      // operations applied so far
    long long score;
    bool operator<(const State &o) const { return score < o.score; }
};

// Total exit-distance of the next K boxes (cheap intermediate proxy).
long long disruptionSum(const int g[20][20]) {
    long long dis = 0;
    for (int r = 0; r < 20; ++r)
        for (int c = 0; c < 20; ++c) {
            int v = g[r][c];
            if (v > curBox && v <= curBox + K_DISRUPT) dis += distm[r][c];
        }
    return dis;
}

// Intermediate (beam-guiding) score: higher is better.
inline long long scoreOf(const State &s) {
    return -((long long)WTURN * (int)s.ops.size()
             + distm[s.tr][s.tc]
             + disruptionSum(s.g));
}

// Greedily deliver boxes [fromBox, fromBox+R) on grid copy g via shortest
// paths; return the total number of operations used.
int rollout(int g[20][20], int fromBox, int R) {
    int total = 0;
    for (int b = fromBox; b < fromBox + R && b < N * N; ++b) {
        int sr = -1, sc = -1;
        for (int r = 0; r < 20 && sr < 0; ++r)
            for (int c = 0; c < 20; ++c)
                if (g[r][c] == b) { sr = r; sc = c; break; }
        if (sr < 0) break;
        int r = sr, c = sc;
        while (distm[r][c] > 0) {
            int gd = distm[r][c];
            int nm[4], nd[4], nr[4], nc[4];
            neighbors(r, c, nm, nd, nr, nc);
            for (int e = 0; e < 4; ++e)
                if (distm[nr[e]][nc[e]] == gd - 1) {
                    rotateG(g, nm[e], nd[e]);
                    r = nr[e]; c = nc[e];
                    ++total;
                    break;
                }
        }
        g[EXR][EXC] = -1;                            // box b delivered & removed
    }
    return total;
}

// Slack-aware Chokudai search; the top NCAND candidates are re-ranked by a
// real rollout of the following ROLLOUT_R boxes. Returns the chosen sequence.
vector<pair<int, int>> deliverSearch(int sr, int sc, int D, int iters) {
    int budget = D + SLACK;
    vector<priority_queue<State>> qs(budget + 1);

    State s0;
    memcpy(s0.g, grid, sizeof grid);
    s0.tr = sr; s0.tc = sc;
    s0.score = scoreOf(s0);
    qs[0].push(s0);

    vector<State> done;                              // completed delivery paths

    for (int it = 0; it < iters; ++it) {
        for (int d = 0; d < budget; ++d) {
            if (qs[d].empty()) continue;
            State s = qs[d].top(); qs[d].pop();
            int remaining = budget - d;
            int nm[4], nd[4], nr[4], nc[4];
            neighbors(s.tr, s.tc, nm, nd, nr, nc);
            for (int e = 0; e < 4; ++e) {
                int gg = distm[nr[e]][nc[e]];
                if (gg > remaining - 1) continue;
                State ns = s;
                rotateG(ns.g, nm[e], nd[e]);
                ns.tr = nr[e]; ns.tc = nc[e];
                ns.ops.push_back({nm[e], nd[e]});
                ns.score = scoreOf(ns);
                if (gg == 0) {
                    if ((int)done.size() < 4000) done.push_back(ns);
                } else {
                    qs[d + 1].push(ns);
                }
            }
        }
    }
    if (done.empty()) return {};

    // re-rank the most promising candidates with a real rollout
    sort(done.begin(), done.end(),
         [](const State &a, const State &b) { return a.score > b.score; });
    int lim = min((int)done.size(), NCAND);
    int bestIdx = 0;
    long long bestTot = LLONG_MAX;
    for (int i = 0; i < lim; ++i) {
        int g2[20][20];
        memcpy(g2, done[i].g, sizeof g2);
        g2[EXR][EXC] = -1;                           // box k removed
        long long tot = (long long)done[i].ops.size()
                       + rollout(g2, curBox + 1, ROLLOUT_R);
        if (tot < bestTot) { bestTot = tot; bestIdx = i; }
    }
    return done[bestIdx].ops;
}

// Fast deterministic fallback: always take a shortest-progress move.
vector<pair<int, int>> deliverGreedy(int sr, int sc) {
    vector<pair<int, int>> ops;
    int r = sr, c = sc;
    while (distm[r][c] > 0) {
        int g = distm[r][c];
        int nm[4], nd[4], nr[4], nc[4];
        neighbors(r, c, nm, nd, nr, nc);
        for (int e = 0; e < 4; ++e)
            if (distm[nr[e]][nc[e]] == g - 1) {
                ops.push_back({nm[e], nd[e]});
                r = nr[e]; c = nc[e];
                break;
            }
    }
    return ops;
}

int main() {
    K_DISRUPT  = envInt("AHC_K", K_DISRUPT);
    ITERS      = envInt("AHC_ITERS", ITERS);
    SLACK      = envInt("AHC_SLACK", SLACK);
    WTURN      = envInt("AHC_WTURN", WTURN);
    ROLLOUT_R  = envInt("AHC_R", ROLLOUT_R);
    NCAND      = envInt("AHC_NCAND", NCAND);

    int n;
    if (scanf("%d", &n) != 1) return 0;
    for (int i = 0; i < 20; ++i)
        for (int j = 0; j < 20; ++j) scanf("%d", &grid[i][j]);

    buildBelts();
    buildDist();

    // --- output the belt layout --------------------------------------------
    printf("20\n");
    for (int m = 0; m < 20; ++m) {
        printf("40");
        for (auto &p : belts[m]) printf(" %d %d", p.first, p.second);
        printf("\n");
    }

    auto t0 = chrono::steady_clock::now();
    vector<pair<int, int>> allops;

    int nxt = 0;
    if (grid[EXR][EXC] == 0) { grid[EXR][EXC] = -1; nxt = 1; }  // box 0 pre-removed

    for (; nxt < 400; ++nxt) {
        int sr = -1, sc = -1;
        for (int r = 0; r < 20 && sr < 0; ++r)
            for (int c = 0; c < 20; ++c)
                if (grid[r][c] == nxt) { sr = r; sc = c; break; }

        int D = distm[sr][sc];
        curBox = nxt;
        vector<pair<int, int>> seq;

        double el = chrono::duration<double>(chrono::steady_clock::now() - t0).count();

        if (D == 0) {
            int safem = 0;
            for (int m = 0; m < 20; ++m)
                if (m != hbelt[sr][sc] && m != vbelt[sr][sc]) { safem = m; break; }
            seq.push_back({safem, 1});
        } else if (el < TIME_LIMIT) {
            int iters = el < 1.0 ? ITERS
                      : (el < 1.5 ? max(1, ITERS * 45 / 100)
                                  : max(1, ITERS * 15 / 100));
            seq = deliverSearch(sr, sc, D, iters);
            if (seq.empty()) seq = deliverGreedy(sr, sc);
        } else {
            seq = deliverGreedy(sr, sc);
        }

        for (auto [m, d] : seq) rotateG(grid, m, d);
        grid[EXR][EXC] = -1;
        for (auto &o : seq) allops.push_back(o);
    }

    printf("%d\n", (int)allops.size());
    for (auto &o : allops) printf("%d %d\n", o.first, o.second);
    return 0;
}

Submission Info

Submission Time
Task A - Conveyor Design
User nemuilemon
Language C++23 (GCC 15.2.0)
Score 886122258
Code Size 12135 Byte
Status AC
Exec Time 1138 ms
Memory 24532 KiB

Judge Result

Set Name test_ALL
Score / Max Score 886122258 / 15000000000
Status
AC × 150
Set Name Test Cases
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
Case Name Status Exec Time Memory
test_0000.txt AC 1086 ms 21576 KiB
test_0001.txt AC 1110 ms 21472 KiB
test_0002.txt AC 1074 ms 21024 KiB
test_0003.txt AC 1037 ms 19304 KiB
test_0004.txt AC 1115 ms 21352 KiB
test_0005.txt AC 1100 ms 19680 KiB
test_0006.txt AC 1111 ms 21200 KiB
test_0007.txt AC 1040 ms 23328 KiB
test_0008.txt AC 1130 ms 21176 KiB
test_0009.txt AC 1106 ms 21680 KiB
test_0010.txt AC 1050 ms 23996 KiB
test_0011.txt AC 1138 ms 21052 KiB
test_0012.txt AC 1093 ms 23524 KiB
test_0013.txt AC 1026 ms 20668 KiB
test_0014.txt AC 1104 ms 21160 KiB
test_0015.txt AC 1093 ms 21128 KiB
test_0016.txt AC 1095 ms 22640 KiB
test_0017.txt AC 1081 ms 21360 KiB
test_0018.txt AC 1098 ms 23112 KiB
test_0019.txt AC 1067 ms 20024 KiB
test_0020.txt AC 1074 ms 21080 KiB
test_0021.txt AC 1108 ms 20820 KiB
test_0022.txt AC 1045 ms 19692 KiB
test_0023.txt AC 1090 ms 20044 KiB
test_0024.txt AC 1102 ms 20924 KiB
test_0025.txt AC 1120 ms 21912 KiB
test_0026.txt AC 1072 ms 22552 KiB
test_0027.txt AC 1074 ms 20444 KiB
test_0028.txt AC 1116 ms 20892 KiB
test_0029.txt AC 1085 ms 21884 KiB
test_0030.txt AC 1093 ms 21040 KiB
test_0031.txt AC 1069 ms 21644 KiB
test_0032.txt AC 1073 ms 20548 KiB
test_0033.txt AC 1074 ms 23360 KiB
test_0034.txt AC 1053 ms 23292 KiB
test_0035.txt AC 1099 ms 21636 KiB
test_0036.txt AC 1068 ms 23928 KiB
test_0037.txt AC 1037 ms 21476 KiB
test_0038.txt AC 1104 ms 21040 KiB
test_0039.txt AC 1030 ms 22096 KiB
test_0040.txt AC 1072 ms 21228 KiB
test_0041.txt AC 1101 ms 21332 KiB
test_0042.txt AC 1075 ms 23320 KiB
test_0043.txt AC 1061 ms 22532 KiB
test_0044.txt AC 1046 ms 23104 KiB
test_0045.txt AC 1065 ms 21436 KiB
test_0046.txt AC 1085 ms 23320 KiB
test_0047.txt AC 1052 ms 20844 KiB
test_0048.txt AC 1026 ms 20804 KiB
test_0049.txt AC 1107 ms 22476 KiB
test_0050.txt AC 1125 ms 20860 KiB
test_0051.txt AC 1043 ms 21832 KiB
test_0052.txt AC 1104 ms 20424 KiB
test_0053.txt AC 1092 ms 21764 KiB
test_0054.txt AC 1049 ms 20480 KiB
test_0055.txt AC 1056 ms 20996 KiB
test_0056.txt AC 1071 ms 22644 KiB
test_0057.txt AC 1043 ms 21208 KiB
test_0058.txt AC 1045 ms 24116 KiB
test_0059.txt AC 1063 ms 23232 KiB
test_0060.txt AC 1089 ms 20408 KiB
test_0061.txt AC 1102 ms 20116 KiB
test_0062.txt AC 1072 ms 22516 KiB
test_0063.txt AC 1065 ms 22496 KiB
test_0064.txt AC 1081 ms 23084 KiB
test_0065.txt AC 1107 ms 24092 KiB
test_0066.txt AC 1054 ms 21700 KiB
test_0067.txt AC 1080 ms 23900 KiB
test_0068.txt AC 1098 ms 21104 KiB
test_0069.txt AC 1054 ms 23424 KiB
test_0070.txt AC 1106 ms 22312 KiB
test_0071.txt AC 1029 ms 21520 KiB
test_0072.txt AC 1092 ms 24360 KiB
test_0073.txt AC 1104 ms 22896 KiB
test_0074.txt AC 1090 ms 21168 KiB
test_0075.txt AC 1068 ms 23516 KiB
test_0076.txt AC 1063 ms 20716 KiB
test_0077.txt AC 1066 ms 19988 KiB
test_0078.txt AC 1085 ms 20024 KiB
test_0079.txt AC 1082 ms 19544 KiB
test_0080.txt AC 1058 ms 21436 KiB
test_0081.txt AC 1071 ms 22016 KiB
test_0082.txt AC 1056 ms 21360 KiB
test_0083.txt AC 1097 ms 23136 KiB
test_0084.txt AC 1065 ms 23728 KiB
test_0085.txt AC 1081 ms 19660 KiB
test_0086.txt AC 1124 ms 21372 KiB
test_0087.txt AC 1099 ms 20844 KiB
test_0088.txt AC 1059 ms 20452 KiB
test_0089.txt AC 1060 ms 22660 KiB
test_0090.txt AC 1095 ms 20856 KiB
test_0091.txt AC 1085 ms 23020 KiB
test_0092.txt AC 1101 ms 21128 KiB
test_0093.txt AC 1072 ms 20600 KiB
test_0094.txt AC 1080 ms 24400 KiB
test_0095.txt AC 1099 ms 21524 KiB
test_0096.txt AC 1030 ms 23408 KiB
test_0097.txt AC 1052 ms 23056 KiB
test_0098.txt AC 1077 ms 22104 KiB
test_0099.txt AC 1049 ms 21568 KiB
test_0100.txt AC 1072 ms 22692 KiB
test_0101.txt AC 1077 ms 20196 KiB
test_0102.txt AC 1085 ms 20832 KiB
test_0103.txt AC 1125 ms 21232 KiB
test_0104.txt AC 1060 ms 24104 KiB
test_0105.txt AC 1085 ms 22868 KiB
test_0106.txt AC 1026 ms 24092 KiB
test_0107.txt AC 1079 ms 22556 KiB
test_0108.txt AC 1077 ms 23560 KiB
test_0109.txt AC 1093 ms 19332 KiB
test_0110.txt AC 1116 ms 22512 KiB
test_0111.txt AC 1072 ms 19576 KiB
test_0112.txt AC 1043 ms 20356 KiB
test_0113.txt AC 1072 ms 23200 KiB
test_0114.txt AC 1076 ms 21992 KiB
test_0115.txt AC 1060 ms 21884 KiB
test_0116.txt AC 1090 ms 23472 KiB
test_0117.txt AC 1080 ms 20900 KiB
test_0118.txt AC 1093 ms 21808 KiB
test_0119.txt AC 1105 ms 23164 KiB
test_0120.txt AC 1091 ms 21336 KiB
test_0121.txt AC 1089 ms 21368 KiB
test_0122.txt AC 1091 ms 19388 KiB
test_0123.txt AC 1045 ms 23016 KiB
test_0124.txt AC 1103 ms 20292 KiB
test_0125.txt AC 1083 ms 21880 KiB
test_0126.txt AC 1073 ms 22748 KiB
test_0127.txt AC 1071 ms 22080 KiB
test_0128.txt AC 1084 ms 23172 KiB
test_0129.txt AC 1080 ms 22024 KiB
test_0130.txt AC 1066 ms 24532 KiB
test_0131.txt AC 1043 ms 22140 KiB
test_0132.txt AC 1075 ms 21760 KiB
test_0133.txt AC 1106 ms 22540 KiB
test_0134.txt AC 1078 ms 20436 KiB
test_0135.txt AC 1082 ms 23292 KiB
test_0136.txt AC 1076 ms 22924 KiB
test_0137.txt AC 1095 ms 22280 KiB
test_0138.txt AC 1029 ms 23336 KiB
test_0139.txt AC 1066 ms 21636 KiB
test_0140.txt AC 1078 ms 22484 KiB
test_0141.txt AC 1061 ms 21880 KiB
test_0142.txt AC 1079 ms 21196 KiB
test_0143.txt AC 1105 ms 18808 KiB
test_0144.txt AC 1072 ms 21520 KiB
test_0145.txt AC 1092 ms 19908 KiB
test_0146.txt AC 1119 ms 21456 KiB
test_0147.txt AC 1080 ms 20040 KiB
test_0148.txt AC 1102 ms 21748 KiB
test_0149.txt AC 1087 ms 20460 KiB