1 条题解

  • 0
    @ 2026-5-14 17:46:29

    题目解析

    问题重述

    nn 个宝箱,第 ii 个宝箱初始有 aia_i 枚硬币。你可以向任意宝箱添加非负整数数量的硬币,但添加后所有宝箱的硬币总数必须至少为 kk

    然后 Monocarp 会贪婪地取宝箱:每次选择当前硬币数最多的宝箱,直到取到的硬币总数 k\ge k 为止。

    你需要通过添加硬币,使得 Monocarp 停止时取到的硬币数恰好等于 kk。求最少需要添加多少硬币。

    关键思路

    核心观察: 假设我们不添加任何硬币,Monocarp 会按照初始硬币数从大到小依次取宝箱。设他取完若干个最大宝箱后,总数为 ss,且 sks \le k,但再取下一个宝箱就会超过 kk。此时,如果我们将目前取到的宝箱中最大的那个增加 ksk-s 枚硬币,那么:

    • 这组宝箱的总数变为 s+(ks)=ks + (k-s) = k
    • 由于最大宝箱变大了,它仍会被先取到,且取完这组宝箱后总数刚好 kk,Monocarp 就会停止
    • 添加的硬币数就是 ksk-s

    为什么只考虑增大当前已取宝箱中的最大值?

    假设我们试图增大某个不在当前集合中的宝箱 ii(初始值为 aia_i),使得它代替集合中的某个宝箱 jjaj>aia_j > a_i)被取到。为了让宝箱 iijj 之前被取,必须满足 ai+xaja_i + x \ge a_j,需要添加 xajaix \ge a_j - a_i。而如果我们直接增大宝箱 jj,添加更少的硬币(ksk-s)就能达到目的。因此,改变取宝箱的顺序不是最优的。

    结论: 最优策略是:

    1. aa 从大到小排序
    2. 从大到小累加,找到最大的 sks \le k,即取前若干个最大宝箱,使得总和不超过 kk 但再加下一个就会超过
    3. 答案就是 ksk - s

    算法步骤

    1. 读入 tt 个测试用例
    2. 对每个测试用例:
      • 读入 nnkk
      • 读入数组 aa,长度为 nn
      • aa 按从大到小排序
      • 初始化 sum=0sum = 0
      • 遍历排序后的数组:
        • 如果 sum+a[i]ksum + a[i] \le k,则 sum+=a[i]sum += a[i]
        • 否则跳出循环
      • 输出 ksumk - sum

    时间复杂度

    排序:O(nlogn)O(n \log n)n50n \le 50,非常快。 总复杂度:O(tnlogn)O(t \cdot n \log n)

    示例验证

    例1: n=5,k=4,a=[4,1,2,3,2]n=5, k=4, a=[4,1,2,3,2]

    • 排序降序:[4,3,2,2,1][4,3,2,2,1]
    • 44sum=4ksum=4 \le k,下一个 33 会使 sum=7>4sum=7>4,停止
    • ans=44=0ans = 4-4=0

    例2: n=5,k=10,a=[4,1,2,3,2]n=5, k=10, a=[4,1,2,3,2]

    • 排序降序:[4,3,2,2,1][4,3,2,2,1]
    • 44sum=4sum=4;取 33sum=7sum=7;取 22sum=9sum=9;下一个 22 会使 sum=11>10sum=11>10,停止
    • ans=109=1ans = 10-9=1

    例3: n=2,k=10,a=[1,1]n=2, k=10, a=[1,1]

    • 排序降序:[1,1][1,1]
    • 11sum=1sum=1;取 11sum=2sum=2;结束(没有更多宝箱)
    • ans=102=8ans = 10-2=8

    例4: n=3,k=8,a=[3,3,3]n=3, k=8, a=[3,3,3]

    • 排序降序:[3,3,3][3,3,3]
    • 33sum=3sum=3;取 33sum=6sum=6;下一个 33 会使 sum=9>8sum=9>8,停止
    • ans=86=2ans = 8-6=2

    C++ 代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int t;
        cin >> t;
        
        while (t--) {
            int n, k;
            cin >> n >> k;
            
            vector<int> a(n);
            for (int i = 0; i < n; i++) {
                cin >> a[i];
            }
            
            // 按从大到小排序
            sort(a.begin(), a.end(), greater<int>());
            
            int sum = 0;
            for (int i = 0; i < n; i++) {
                if (sum + a[i] <= k) {
                    sum += a[i];
                } else {
                    break;
                }
            }
            
            cout << k - sum << '\n';
        }
        
        return 0;
    }
    
    • 1

    信息

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