4 条题解

  • 0
    @ 2026-9-27 12:00:51

    满分解法(100 分)

    从一次得分看卡牌的用途

    得分后只留下刚放入的一张牌。因此,第一次得分至少要放三张牌,此后每次得分至少再放两张牌。另一方面,保留下来的牌会参与下一次得分,同一张牌可以被利用两次,不能把每次得分理解成互不相交的三张牌。

    例如,将两张黑牌、两张白牌和一张青牌按“黑、白、青、黑、白”放入:第三张牌触发第一次得分并留下青牌,第五张牌触发第二次得分。同一张青牌参与了这两次得分。

    三种颜色地位相同。将数量排序,记为 a≤b≤ca\le b\le c,对应颜色记为 A,B,CA,B,C,总张数为 N=a+b+cN=a+b+c。先求得分的上界,再证明这些上界的最小值一定能够达到。

    三个独立的上界

    假设获得 kk 分。

    总张数。 第一次得分至少使用三张新牌,以后每次至少使用两张新牌,所以

    $$2k+1\le N,\qquad k\le\left\lfloor\frac{N-1}{2}\right\rfloor.$$

    最少的一种颜色。 一张牌第一次参与得分时,只有它恰好是刚放入的牌,才会被保留;它再次参与得分时必然被销毁。因此每张牌至多参与两次得分,而每次得分都需要颜色 AA,所以

    k≤2a.k\le 2a.

    最少的两种颜色。 每次得分至少销毁一张 AA 或 BB 色牌。观察最后一次得分:如果最后留下 CC 色牌,这次会同时销毁至少一张 AA 和一张 BB,总共至少销毁 k+1k+1 张这两种颜色的牌;如果最后留下 AA 或 BB 色牌,至少有 kk 张这两种颜色的牌被销毁,外加最后留下的一张。两种情况都要求

    k+1≤a+b,k≤a+b−1.k+1\le a+b,\qquad k\le a+b-1.

    第三个上界不能省略。例如 (a,b,c)=(1,1,6)(a,b,c)=(1,1,6) 时,前两个上界分别是 33 和 22,但实际只能得 11 分:第一次得分后,两种稀缺颜色至少有一种已经耗尽。

    因此答案不超过

    $$K=\min\left(\left\lfloor\frac{a+b+c-1}{2}\right\rfloor,\ 2a,\ a+b-1\right).$$

    用相邻得分之间的共享牌构造方案

    下面证明任意满足三个上界的正整数 kk 都可达到。k=1k=1 时直接放入三种颜色各一张即可,以下考虑 k≥2k\ge2。

    把每次得分所需的三色牌各记为一个三元组。让相邻两个三元组共享前一次得分留下的那张牌,这样总共只使用 3k−(k−1)=2k+13k-(k-1)=2k+1 张实体牌。共有 L=k−1L=k-1 张共享牌,将它们依次记为 q1,…,qLq_1,\ldots,q_L。

    相邻共享牌的颜色必须不同:在一次中间得分中,前一次留下的牌已经在堆中,后一次留下的牌必须是另外一种颜色,才能补齐三色。反过来,只要共享牌序列没有相邻同色,就能恢复合法投放:第一个三元组先放另两色,再放 q1q_1;中间三元组在已有 qi−1q_{i-1} 的基础上,先放第三种颜色,再放 qiq_i;最后一个三元组放入当前缺少的两种颜色。

    设三种颜色各被选作共享牌 tA,tB,tCt_A,t_B,t_C 次,则实际使用的三色牌数分别为

    k−tA,k−tB,k−tC.k-t_A,\quad k-t_B,\quad k-t_C.

    因此,我们要找到总和为 LL 的非负整数 tA,tB,tCt_A,t_B,t_C,满足

    $$t_A\ge\ell_A=\max(0,k-a),\quad t_B\ge\ell_B=\max(0,k-b),\quad t_C\ge\ell_C=\max(0,k-c),$$

    并且能排成没有相邻同色的共享牌序列。

    长度为 LL 的序列能够这样排列,当且仅当每种颜色的数量都不超过 h=⌈L/2⌉h=\lceil L/2\rceil。必要性来自相同颜色之间要用别的颜色隔开。充分性可以这样构造:按颜色数量从多到少,依次填入第 1,3,5,…1,3,5,\ldots 个位置,再填第 2,4,6,…2,4,6,\ldots 个位置。同一段内同色牌隔位出现;跨越奇偶两段的那种颜色数量严格小于 hh,它占用的奇数位置和偶数位置之间仍隔着至少两个位置,不会相邻。数量等于 hh 的颜色会完整占满首段,或在首段填完后才开始,不会跨段。

    接下来检查三个上界恰好保证这些要求。

    上界保证共享牌数量能够分配

    首先,由 k≤2ak\le2a 及 a≤b≤ca\le b\le c,每个计数下界都不超过 hh。具体地,a≥⌈k/2⌉a\ge\lceil k/2\rceil,因此

    $$\ell_A\le k-\left\lceil\frac{k}{2}\right\rceil =\left\lceil\frac{k-1}{2}\right\rceil=h,$$

    而 ℓB,ℓC≤ℓA\ell_B,\ell_C\le\ell_A。

    其次,需要证明 ℓA+ℓB+ℓC≤L=k−1\ell_A+\ell_B+\ell_C\le L=k-1。按正下界的数量分类:

    • 三个都为正时,总和为 3k−N3k-N。由 2k+1≤N2k+1\le N,它不超过 k−1k-1。
    • 两个为正时,必然对应 a,ba,b,总和为 2k−a−b2k-a-b。由 k≤a+b−1k\le a+b-1,它不超过 k−1k-1。
    • 只有一个为正时,总和为 k−a≤k−1k-a\le k-1;没有正下界时总和为 00。

    从三个下界出发,逐个增加计数,直到总和达到 LL,过程中不让任何计数超过 hh。因为下界之和不超过 LL,而总容量 3h≥L3h\ge L,这个补足过程一定可以完成。

    这样得到的共享牌序列可以按上一节恢复成合法的 kk 次得分,且每种颜色实际使用的牌数都不超过现有数量。最后把未使用的牌继续放入牌堆,不会减少已经获得的分数。因此 KK 不仅是上界,也确实可达。

    计算答案

    排序三个数量,直接计算

    $$\boxed{\min\left(\left\lfloor\frac{a+b+c-1}{2}\right\rfloor,\ 2a,\ a+b-1\right)}.$$

    构造只用于证明,程序不需要实际生成投放顺序。

    时间复杂度:O(T)O(T),每组只排序三个数并计算三个表达式。

    空间复杂度:O(1)O(1),各组独立处理,只保存三个数量。

    AC 代码(C++20)

    #include <bits/stdc++.h>
    using namespace std;
    using i64 = long long;
    
    void solve() {
      vector<i64> a(3);
      for (i64& x : a) cin >> x;
      sort(a.begin(), a.end());
      i64 sum = accumulate(a.begin(), a.end(), 0LL);
      cout << min({(sum - 1) / 2, 2 * a[0], a[0] + a[1] - 1}) << '\n';
    }
    
    int main() {
      cin.tie(0)->sync_with_stdio(0);
      int T;
      cin >> T;
      while (T--) solve();
    }
    
    • 0
      @ 2026-9-27 12:00:50

      两种较多颜色等量(通过子任务 1、5–6,共 40 分)

      两种较多颜色等量时的上界

      考虑 X≤Y=ZX\le Y=Z 的情况,包含子任务 1,5,61,5,6。记 x=X,y=Y=Zx=X,y=Y=Z。这里黑牌数量最少,白牌与青牌等量。

      每次得分后的保留牌可以再参加一次得分,但第二次之后必定被销毁。因此一张黑牌至多参与两次得分,答案不超过 2x2x。第一次得分至少需要三张新牌,此后每次至少需要两张新牌,所以答案还不超过 ⌊(x+2y−1)/2⌋\lfloor(x+2y-1)/2\rfloor。

      下面证明在这个等量条件下,两项限制已经足够,答案就是

      $$K=\min\left(2x,\left\lfloor\frac{x+2y-1}{2}\right\rfloor\right).$$

      证明两个上界可以同时达到

      K=1K=1 时放入三色各一张即可。设 k=K≥2k=K\ge2,把目标的 kk 次得分各记为一个三色三元组。相邻两个三元组共享前一次留下的实体牌,共有 L=k−1L=k-1 张共享牌;相邻共享牌颜色不同,才允许中间三元组先使用旧的保留牌,最后留下另一种颜色。

      若某种颜色被共享 tt 次,它在 kk 个三元组中的实际用量就是 k−tk-t。所以黑色共享次数至少为 u=max⁡(0,k−x)u=\max(0,k-x),白色和青色的共享次数分别至少为 v=max⁡(0,k−y)v=\max(0,k-y)。因为 x≤yx\le y,有 v≤uv\le u。

      没有相邻同色的长度为 LL 的序列要求每色最多出现 h=⌈L/2⌉h=\lceil L/2\rceil 次。由 k≤2xk\le2x,得到 u≤hu\le h,因此 v≤hv\le h。

      这些下界的总和也不会超过 LL:若 k≤yk\le y,只有黑色可能需要共享,u+2v=u≤k−1u+2v=u\le k-1;若 k>yk>y,三色都需要共享,此时

      u+2v=3k−x−2y≤k−1,u+2v=3k-x-2y\le k-1,

      最后一步正是利用总数上界 2k+1≤x+2y2k+1\le x+2y。

      从三个下界开始补足到总数 LL,每个计数都不超过 hh。总容量 3h≥L3h\ge L,所以一定能补足。这样的三色计数可以排成没有相邻同色的序列:按颜色数量降序,依次填奇数位置、再填偶数位置。同色在各段内隔位出现,跨段的颜色数量小于 hh,两段占据的位置不会相邻。

      得到共享牌序列后,第一组先放另外两色、最后放第一张共享牌;中间每组在旧共享牌之后,先放第三色、最后放下一张共享牌;最后补入缺少的两色。这样获得 kk 分,各色用量都不超过现有数量,剩余牌最后继续放入即可。

      这个做法依赖 X≤Y=ZX\le Y=Z。例如 (X,Y,Z)=(1,1,6)(X,Y,Z)=(1,1,6) 不满足该条件,两个上界取最小会得到 22,实际却只有 11 分;因此不能把本式直接用作一般情况的答案。

      当 X=Y=Z=xX=Y=Z=x 时,式子简化为 ⌊(3x−1)/2⌋\lfloor(3x-1)/2\rfloor,所以相同代码自然覆盖等量子任务,无需再写一份算法。

      时间复杂度:O(T)O(T)。空间复杂度:O(1)O(1)。

      参考代码(C++20)

      #include <bits/stdc++.h>
      using namespace std;
      using i64 = long long;
      
      void solve() {
        i64 x, y, z;
        cin >> x >> y >> z;
        cout << min(2 * x, (x + y + z - 1) / 2) << '\n';
      }
      
      int main() {
        cin.tie(0)->sync_with_stdio(0);
        int T;
        cin >> T;
        while (T--) solve();
      }
      
      • 0
        @ 2026-9-27 12:00:50

        按剩余数量记忆化(通过子任务 1–4,共 51 分)

        只保留有用的投放

        在 X,Y,Z≤50X,Y,Z\le50 时,可以按三色剩余数量记忆化搜索,覆盖子任务 1,2,3,41,2,3,4。

        第一次得分需要三种颜色各一张,最后放哪一种就留下哪一种。一次得分以后,牌堆中只有一张牌。为了再次得分,只需要再放入另外两种颜色各一张,并选择其中哪一种最后放入。

        为什么可以不考虑提前放入的多余牌?在任意投放方案中,两次得分之间重复放入同一种颜色,只会使它们在下一次得分时一起销毁;保留每个必需颜色的一张,并维持原本最后放入的颜色,仍会产生相同的得分和相同的保留牌。所有删去的投放移到已有得分完成之后即可。因此,存在一个最优方案,第一次恰用三张新牌,此后每次恰用两张新牌。

        状态与转移

        将黑、白、青依次编号为 0,1,20,1,2。定义 f(x,y,z,c)f(x,y,z,c) 为:还有 x,y,zx,y,z 张牌尚未投放,牌堆中恰有一张颜色 cc 的牌时,之后最多还能获得的分数。保留牌不计入 x,y,zx,y,z。

        如果 c=0c=0,下一次得分必须各取一张白牌、青牌,可以最后放白牌或青牌,所以

        $$f(x,y,z,0)=1+\max\{f(x,y-1,z-1,1),f(x,y-1,z-1,2)\}\quad(y,z>0).$$

        同理,另外两种保留颜色的转移为

        $$f(x,y,z,1)=1+\max\{f(x-1,y,z-1,0),f(x-1,y,z-1,2)\}\quad(x,z>0),$$$$f(x,y,z,2)=1+\max\{f(x-1,y-1,z,0),f(x-1,y-1,z,1)\}\quad(x,y>0).$$

        当对应的两个剩余数量中有一个为 00 时,无法再凑齐三色,状态值为 00。其余未计算状态用 −1-1 标记。

        每次递归使剩余总数减少 22,因此没有循环。同一状态以后的选择只由三个剩余数量和保留颜色决定,与之前的投放顺序无关,可以直接记忆化。

        第一次用掉三色各一张并得到一分,保留颜色可以任选,最终答案为

        1+max⁡c∈{0,1,2}f(X−1,Y−1,Z−1,c).1+\max_{c\in\{0,1,2\}} f(X-1,Y-1,Z-1,c).

        例如 (X,Y,Z)=(2,2,1)(X,Y,Z)=(2,2,1),第一次留下青牌后到达 f(1,1,0,2)f(1,1,0,2),还能取一张黑牌和一张白牌,再得一分,总分为 22。第一次留下黑牌时,剩余青牌为零,就不能继续得分。

        复杂度

        初次得分后三个坐标范围分别为 0…X−10\ldots X-1、0…Y−10\ldots Y-1、0…Z−10\ldots Z-1,保留全部 3XYZ3XYZ 个状态,每个状态只有两个后继。按测试用例分别建立、释放状态表。

        时间复杂度:O ⁣(∑t=1TXtYtZt)O\!\left(\sum_{t=1}^{T}X_tY_tZ_t\right),包括状态表初始化。

        空间复杂度:O(max⁡tXtYtZt)O(\max_t X_tY_tZ_t),状态表和递归栈均计入。大计数下此表无法存储,方法只保证上述受限子任务。

        参考代码(C++20)

        #include <bits/stdc++.h>
        using namespace std;
        using i64 = long long;
        
        void solve() {
          int x, y, z;
          cin >> x >> y >> z;
          vector dp(3, vector(x, vector(y, vector<int>(z, -1))));
          auto dfs = [&](auto&& self, int x, int y, int z, int color) -> int {
            int& res = dp[color][x][y][z];
            if (res != -1) return res;
            res = 0;
            if (color == 0 && y && z) {
              res = 1 + max(self(self, x, y - 1, z - 1, 1), self(self, x, y - 1, z - 1, 2));
            }
            if (color == 1 && x && z) {
              res = 1 + max(self(self, x - 1, y, z - 1, 0), self(self, x - 1, y, z - 1, 2));
            }
            if (color == 2 && x && y) {
              res = 1 + max(self(self, x - 1, y - 1, z, 0), self(self, x - 1, y - 1, z, 1));
            }
            return res;
          };
          int ans = 0;
          for (int i = 0; i < 3; i++) ans = max(ans, dfs(dfs, x - 1, y - 1, z - 1, i));
          cout << ans + 1 << '\n';
        }
        
        int main() {
          cin.tie(0)->sync_with_stdio(0);
          int T;
          cin >> T;
          while (T--) solve();
        }
        
        • 0
          @ 2026-9-27 12:00:50

          枚举投放顺序(通过子任务 1–2,共 11 分)

          当 X+Y+Z≤8X+Y+Z\le8 时,可以枚举全部投放顺序,覆盖子任务 1,21,2。

          同色卡牌没有区别。建立一个包含 XX 个 00、YY 个 11、ZZ 个 22 的升序序列,用全排列枚举每一种不同的颜色排列。对每个排列从空牌堆开始模拟,记录当前牌堆中出现的颜色集合以及已经获得的分数。

          用三位二进制数表示颜色集合。放入颜色 ii 后,将第 ii 位置为 11。集合变成 1112111_2 时增加一分,并将集合改为只包含刚放入的颜色,即 2i2^i。这里不能清空集合,因为新牌仍留在堆中。

          例如“黑、白、青、黑、白”的集合依次为 0012,0112,1002,1012,0102001_2,011_2,100_2,101_2,010_2,第三步和第五步各得一分;两次得分后的集合都只保留最后一种颜色。

          每种合法投放顺序恰好对应一个颜色排列,模拟又完全执行题面规则,所以对所有排列得分取最大值就是答案。

          记 N=X+Y+ZN=X+Y+Z,不同排列的数量为 N!/(X!Y!Z!)N!/(X!Y!Z!)。虽然 N≤16N\le16 时也能用相同方法算出正确答案,但排列数量增长很快,在最大 TT 下不能保证原时限。

          时间复杂度:$O\!\left(\sum_{t=1}^{T}N_t\frac{N_t!}{X_t!Y_t!Z_t!}\right)$,每个排列扫描全部卡牌。

          空间复杂度:O(max⁡tNt)O(\max_t N_t),各组依次处理,仅保存当前颜色排列。

          参考代码(C++20)

          #include <bits/stdc++.h>
          using namespace std;
          using i64 = long long;
          
          void solve() {
            vector<int> cnt(3), a;
            for (int& x : cnt) cin >> x;
            for (int i = 0; i < 3; i++) a.insert(a.end(), cnt[i], i);
            int ans = 0;
            do {
              int mask = 0, sum = 0;
              for (int x : a) {
                mask |= 1 << x;
                if (mask == 7) {
                  sum++;
                  mask = 1 << x;
                }
              }
              ans = max(ans, sum);
            } while (next_permutation(a.begin(), a.end()));
            cout << ans << '\n';
          }
          
          int main() {
            cin.tie(0)->sync_with_stdio(0);
            int T;
            cin >> T;
            while (T--) solve();
          }
          
          • 1

          信息

          ID
          2310
          时间
          1000ms
          内存
          512MiB
          难度
          9
          标签
          (无)
          递交数
          93
          已通过
          20
          上传者