結果

問題 No.3418 【絶望】30個並列ごちゃ混ぜHit&Blowで遊ぼう!
コンテスト
ユーザー butsurizuki
提出日時 2025-12-14 19:46:17
言語 C++23
(gcc 13.3.0 + boost 1.89.0)
結果
TLE  
実行時間 -
コード長 6,707 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,106 ms
コンパイル使用メモリ 201,380 KB
実行使用メモリ 26,228 KB
スコア 9,291,800
平均クエリ数 80.28
最終ジャッジ日時 2025-12-25 01:40:33
合計ジャッジ時間 377,066 ms
ジャッジサーバーID
(参考情報)
judge3 / judge2
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 93 TLE * 7
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

// By Gemini 3 Pro 2nd attempt
/**
 * 30 Parallel Mixed Hit & Blow Solver (High Accuracy Ver.)
 * * Strategy:
 * 1. Maintain a set of possible candidates for the *remaining* hidden strings.
 * 2. Select the next query by minimizing the sum of squared group sizes (Entropy Maximization).
 * - If candidates are few (< 2000), evaluate ALL candidates as potential queries.
 * - If many, sample a larger subset (e.g., 200) to estimate the best query.
 * 3. Filter candidates based on the "Bag" of responses, carefully excluding
 * the (5,0) responses generated by already-found strings.
 */

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <random>
#include <chrono>
#include <map>

using namespace std;

// Holds Hit & Blow result
struct Result {
    int h, b;
    bool operator==(const Result& other) const {
        return h == other.h && b == other.b;
    }
};

// Calculate Hit & Blow
Result calc_hb(const string& secret, const string& query) {
    int h = 0;
    int b = 0;
    // Hit count
    for (int i = 0; i < 5; ++i) {
        if (secret[i] == query[i]) h++;
    }
    // Blow count
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            if (i != j && secret[i] == query[j]) b++;
        }
    }
    return {h, b};
}

// Generate valid strings (digits 0-9, unique, length 5)
vector<string> generate_all_candidates() {
    vector<string> s;
    for (int i = 0; i <= 99999; ++i) {
        string cur = to_string(i);
        while (cur.length() < 5) cur = "0" + cur;
        
        // Check uniqueness
        int mask = 0;
        bool ok = true;
        for (char c : cur) {
            int d = c - '0';
            if ((mask >> d) & 1) {
                ok = false;
                break;
            }
            mask |= (1 << d);
        }
        if (ok) s.push_back(cur);
    }
    return s;
}

int main() {
    // Fast I/O
    cin.tie(NULL);
    ios_base::sync_with_stdio(false);

    auto candidates = generate_all_candidates();
    int total_targets = 30;
    int found_count = 0;

    // Random engine
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

    // Loop until all 30 strings are found
    while (found_count < total_targets) {
        // --- 1. Select the Best Query ---
        
        string query_str;
        
        // Dynamic sampling strategy
        // If candidates are few, check all to be precise.
        // If many, sample to save time (though 5s is generous, O(N^2) is still heavy at 30k).
        int cand_size = candidates.size();
        vector<string> query_pool;
        
        if (cand_size <= 2000) {
            // Full search
            query_pool = candidates;
        } else {
            // Random sampling
            int sample_size = 300; 
            vector<int> indices(cand_size);
            for(int i=0; i<cand_size; ++i) indices[i] = i;
            // Partial shuffle or just pick random
            // Since we need just 300, random pick is faster than full shuffle
            for(int i=0; i<sample_size; ++i) {
                query_pool.push_back(candidates[rng() % cand_size]);
            }
            // Ensure at least one valid candidate is in the pool
            if (query_pool.empty()) query_pool.push_back(candidates[0]);
        }

        double best_score = 1e18;
        string best_query = query_pool[0];

        // Evaluate each potential query in the pool
        for (const string& q : query_pool) {
            // We map (h, b) to index: h*6 + b (max 5*6+5 = 35)
            int counts[36] = {0};
            
            // Simulate response against all current candidates
            for (const string& c : candidates) {
                Result r = calc_hb(c, q);
                counts[r.h * 6 + r.b]++;
            }

            // Calculate "Collision Score" (Sum of Squares)
            // Minimizing this maximizes entropy (spreads candidates evenly)
            long long score = 0;
            for (int k = 0; k < 36; ++k) {
                if (counts[k] > 0) {
                    score += (long long)counts[k] * counts[k];
                }
            }

            if (score < best_score) {
                best_score = score;
                best_query = q;
            }
        }
        
        query_str = best_query;

        // --- 2. Perform Query ---
        cout << query_str << endl; // flush

        // --- 3. Read Response ---
        // Frequency map of received (H, B) pairs
        int response_counts[36] = {0};
        int current_fives = 0;
        
        for (int i = 0; i < total_targets; ++i) {
            int h, b;
            cin >> h >> b;
            if (h == 5 && b == 0) current_fives++;
            response_counts[h * 6 + b]++;
        }

        // Check completion
        if (current_fives == total_targets) {
            return 0;
        }

        // --- 4. Process Found Strings ---
        bool is_new_hit = (current_fives > found_count);
        if (is_new_hit) {
            // We found a new string!
            // Since candidates are distinct and we query one string, we found exactly one.
            found_count = current_fives;
            
            // The query itself was the answer. Remove it from candidates.
            // (We will rebuild candidates anyway, but logically it's gone)
        }

        // --- 5. Filter Candidates ---
        
        // IMPORTANT: The response contains 'found_count' instances of (5,0)
        // that correspond to the strings we have ALREADY found (including potentially the one just found).
        // These provide NO information about the remaining candidates.
        // We must subtract them to see the distribution of the unknown strings.
        response_counts[5 * 6 + 0] -= found_count;

        vector<string> next_candidates;
        next_candidates.reserve(candidates.size());

        for (const string& cand : candidates) {
            // If the candidate is the one we just queried:
            // - If it was a hit, it's now 'found', so don't add to next_candidates.
            // - If it was a miss, it's not in S, so don't add.
            // So in both cases, exclude the query string itself.
            if (cand == query_str) continue;

            // Calculate what (H, B) this candidate would return for the current query
            Result r = calc_hb(cand, query_str);
            int idx = r.h * 6 + r.b;

            // Check consistency:
            // Does the remaining response bag contain this (H, B)?
            if (response_counts[idx] > 0) {
                next_candidates.push_back(cand);
            }
        }

        candidates = next_candidates;
    }

    return 0;
}
0