1 条题解

  • 0
    @ 2025-11-11 10:40:02

    #5457. 「ROI 2012 Day 1」杏干 题解

    问题分析

    我们有 NN 枚金币,其中恰好一枚是较轻的假币。使用魔法天平:

    • 如果两边重量相同,消耗 RR 个杏干
    • 如果两边重量不同,消耗 UU 个杏干,并知道哪边轻

    目标:在最坏情况下保证找出假币,且最小化杏干消耗。

    关键观察

    1. 信息论下界:每次称量最多获得 log23\log_2 3 比特信息(因为结果有3种:左轻、右轻、相等)
    2. 决策树模型:这是一个构建最优决策树的问题,叶子数 N\geq N(每个金币对应一个叶子)
    3. 状态表示:设 f(k)f(k) = 用最优策略在 kk 个金币中找假币的最小最坏代价

    动态规划解法

    状态转移

    假设我们有 nn 个待检测金币。一次称量时:

    • 左边放 aa 个,右边放 aa 个(1an/21 \leq a \leq \lfloor n/2 \rfloor
    • 剩下 n2an-2a 个不参与本次称量

    三种结果:

    1. 左边轻:假币在左边 aa 个中,代价 U+f(a)U + f(a)
    2. 右边轻:假币在右边 aa 个中,代价 U+f(a)U + f(a)
    3. 两边相等:假币在未称的 n2an-2a 个中,代价 R+f(n2a)R + f(n-2a)

    最坏情况代价:

    $$f(n) = \min_{1 \leq a \leq \lfloor n/2 \rfloor} \max(U + f(a), R + f(n-2a)) $$

    边界条件

    • f(0)=0f(0) = 0(没有金币)
    • f(1)=0f(1) = 0(只剩一个金币,必然是假币)

    算法优化

    直接DP复杂度 O(N2)O(N^2) 太高,需要优化。

    观察单调性

    g(a)=max(U+f(a),R+f(n2a))g(a) = \max(U + f(a), R + f(n-2a))

    • U+f(a)U + f(a)aa 增加而增加
    • R+f(n2a)R + f(n-2a)aa 增加而减少

    最优的 aa 在两者交点附近,可以三分查找。

    更高效的实现

    由于 NN 很大,我们利用决策的规律性:

    • 最优的 aa 通常接近 n/3n/3
    • 可以只检查 aa[n/3c,n/3+c][n/3-c, n/3+c] 范围内的值(cc 为小常数)

    代码实现

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 1000005;
    
    long long f[MAXN];
    int N, R, U;
    
    long long cost(int a, int n) {
        return max(1LL * U + f[a], 1LL * R + f[n - 2 * a]);
    }
    
    int main() {
        cin >> N >> R >> U;
        
        f[0] = f[1] = 0;
        
        for (int n = 2; n <= N; n++) {
            long long best = 1LL << 60;
            
            // 在 n/3 附近搜索最优的 a
            int left = max(1, n / 3 - 2);
            int right = min(n / 2, n / 3 + 2);
            
            for (int a = left; a <= right; a++) {
                best = min(best, cost(a, n));
            }
            
            // 检查边界情况
            if (n / 3 - 2 > 1) {
                best = min(best, cost(1, n));
            }
            if (n / 3 + 2 < n / 2) {
                best = min(best, cost(n / 2, n));
            }
            
            f[n] = best;
        }
        
        cout << f[N] << endl;
        
        return 0;
    }
    

    复杂度分析

    • 时间复杂度O(N)O(N),每个 nn 只检查常数个 aa
    • 空间复杂度O(N)O(N)
    • 1

    信息

    ID
    5218
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者