結果

問題 No.3418 【絶望】30個並列ごちゃ混ぜHit&Blowで遊ぼう!
コンテスト
ユーザー butsurizuki
提出日時 2025-12-16 00:08:03
言語 C++23
(gcc 13.3.0 + boost 1.89.0)
結果
AC  
実行時間 3,160 ms / 5,000 ms
コード長 7,882 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 3,441 ms
コンパイル使用メモリ 294,552 KB
実行使用メモリ 26,308 KB
スコア 9,995,841
平均クエリ数 41.59
最終ジャッジ日時 2025-12-25 01:56:57
合計ジャッジ時間 315,992 ms
ジャッジサーバーID
(参考情報)
judge4 / judge2
純コード判定しない問題か言語
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 100
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <bits/stdc++.h>
using namespace std;

// 10P5 = 30240 candidates
static constexpr int R = 36;               // id = h*6 + b
static constexpr int ID_50 = 5 * 6 + 0;    // (5,0)

struct Cand {
    array<uint8_t, 5> d;   // digits
    uint16_t mask;         // 10-bit mask
    string s;              // "01234"
};

struct Constraint {
    int q_idx;                         // which query
    vector<uint8_t> resp;              // resp[i] = id(candidate i vs q)
    array<int16_t, R> cnt;             // histogram for current remaining secrets
};

int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(nullptr);

    // --- Generate all candidates (lexicographic, 30240) ---
    vector<Cand> cand;
    cand.reserve(30240);
    for (int a = 0; a < 10; a++) {
        for (int b = 0; b < 10; b++) if (b != a) {
            for (int c = 0; c < 10; c++) if (c != a && c != b) {
                for (int d = 0; d < 10; d++) if (d != a && d != b && d != c) {
                    for (int e = 0; e < 10; e++) if (e != a && e != b && e != c && e != d) {
                        Cand x;
                        x.d = {(uint8_t)a,(uint8_t)b,(uint8_t)c,(uint8_t)d,(uint8_t)e};
                        x.mask = (uint16_t)((1u<<a)|(1u<<b)|(1u<<c)|(1u<<d)|(1u<<e));
                        x.s.resize(5);
                        x.s[0] = char('0' + a);
                        x.s[1] = char('0' + b);
                        x.s[2] = char('0' + c);
                        x.s[3] = char('0' + d);
                        x.s[4] = char('0' + e);
                        cand.push_back(x);
                    }
                }
            }
        }
    }
    const int N = (int)cand.size();

    // Remaining secrets count m = 30 - solved
    int solved = 0;
    int remaining = 30;

    // weights: expected membership in remaining set, sum(w)=remaining
    vector<double> w(N, remaining / (double)N);
    vector<uint8_t> asked(N, 0);

    vector<Constraint> cons;
    cons.reserve(128);

    // Time control
    const double TIME_LIMIT_SEC = 4.90; // easy to tweak
    const int BASE_IPFP_ITER = 45;       // try 45 when time allows (tweakable)
    const auto t_start = chrono::steady_clock::now();

    auto elapsed_sec = [&]() -> double {
        auto now = chrono::steady_clock::now();
        return chrono::duration<double>(now - t_start).count();
    };

    // Build resp array for a query q_idx: resp[i] = (h*6+b) between candidate i and q
    auto build_resp = [&](int q_idx) -> vector<uint8_t> {
        vector<uint8_t> resp(N);
        const auto &q = cand[q_idx];
        const uint16_t qmask = q.mask;
        const auto &qd = q.d;

        for (int i = 0; i < N; i++) {
            const auto &sd = cand[i].d;
            int h =
                (sd[0] == qd[0]) + (sd[1] == qd[1]) + (sd[2] == qd[2]) +
                (sd[3] == qd[3]) + (sd[4] == qd[4]);
            int common = __builtin_popcount((unsigned)(cand[i].mask & qmask));
            int b = common - h;
            resp[i] = (uint8_t)(h * 6 + b);
        }
        return resp;
    };

    // IPFP update: adjust weights so that for each constraint, expected histogram matches cnt
    auto ipfp_update = [&](int iters) {
        if (cons.empty()) return;

        for (int it = 0; it < iters; it++) {
            for (auto &c : cons) {
                double E[R];
                for (int r = 0; r < R; r++) E[r] = 0.0;

                // expected counts
                const auto &resp = c.resp;
                for (int i = 0; i < N; i++) {
                    double wi = w[i];
                    if (wi == 0.0) continue;
                    E[resp[i]] += wi;
                }

                // scaling factors
                double f[R];
                for (int r = 0; r < R; r++) {
                    if (c.cnt[r] == 0) f[r] = 0.0;
                    else if (E[r] > 0.0) {
                        double val = (double)c.cnt[r] / E[r];
                        // mild clamp for numerical safety
                        if (val > 1e9) val = 1e9;
                        f[r] = val;
                    } else {
                        // Should not happen if constraints are consistent,
                        // but keep safe.
                        f[r] = 0.0;
                    }
                }

                // apply scaling
                for (int i = 0; i < N; i++) {
                    double wi = w[i];
                    if (wi == 0.0) continue;
                    w[i] = wi * f[resp[i]];
                }
            }

            // normalize sum(w)=remaining
            double sum = 0.0;
            for (double wi : w) sum += wi;

            if (!(sum > 0.0) || !isfinite(sum)) {
                // fallback: uniform over unasked
                int cnt_unasked = 0;
                for (int i = 0; i < N; i++) if (!asked[i]) cnt_unasked++;
                if (cnt_unasked == 0) return;
                double uni = remaining / (double)cnt_unasked;
                for (int i = 0; i < N; i++) w[i] = asked[i] ? 0.0 : uni;
            } else {
                double scale = remaining / sum;
                for (double &wi : w) wi *= scale;
            }
        }
    };

    // --- Main interactive loop ---
    while (true) {
        // choose next query = argmax weight among unasked
        int q_idx = -1;
        double best = -1.0;
        for (int i = 0; i < N; i++) {
            if (asked[i]) continue;
            if (w[i] > best) {
                best = w[i];
                q_idx = i;
            }
        }
        if (q_idx < 0) return 0; // should not happen

        asked[q_idx] = 1;

        // output query
        cout << cand[q_idx].s << "\n" << flush;

        // read response (30 pairs)
        array<int16_t, R> total{};
        total.fill(0);

        vector<pair<int,int>> hb(30);
        for (int i = 0; i < 30; i++) {
            int h, b;
            if (!(cin >> h >> b)) return 0;
            hb[i] = {h, b};

            if (h == -1 && b == -1) {
                // invalid interaction -> terminate immediately
                return 0;
            }
            int id = h * 6 + b;
            if (0 <= id && id < R) total[id]++;
        }

        // if (H0,B0)=(5,0) then all are solved (because sorted)
        if (hb[0].first == 5) return 0;

        const int solved_before = solved;
        const int total50 = total[ID_50];

        // detect if this query hit a NEW secret (count of (5,0) increases by 1)
        const bool hit_new = (total50 > solved_before);

        // build resp array for this query (needed for constraints/IPFP)
        vector<uint8_t> resp = build_resp(q_idx);

        if (hit_new) {
            solved++;
            remaining--;

            // this solved string is removed from all previous constraints:
            // cnt[resp_of_solved]--
            for (auto &c : cons) {
                uint8_t r = c.resp[q_idx];
                c.cnt[r]--;
            }
        }

        // counts for remaining secrets after this query
        array<int16_t, R> cnt = total;

        // subtract already-solved-before-query contributions to (5,0)
        cnt[ID_50] -= (int16_t)solved_before;

        // if we newly hit, subtract this q itself from remaining set
        if (hit_new) cnt[ID_50]--;

        // queried string can never be "remaining" any more
        w[q_idx] = 0.0;

        // add new constraint
        Constraint nc;
        nc.q_idx = q_idx;
        nc.resp = std::move(resp);
        nc.cnt = cnt;
        cons.push_back(std::move(nc));

        // decide IPFP iterations by remaining time
        double left = TIME_LIMIT_SEC - elapsed_sec();
        int iters = BASE_IPFP_ITER;
        if (left < 0.40) iters = 1;
        else if (left < 1.00) iters = min(iters, 2);
        else if (left < 2.00) iters = min(iters, 3);

        ipfp_update(iters);
    }
}
0