题解:AtCoder AT_awc0002_d Keys and Treasure Boxes

【题目来源】

AtCoder:D - Keys and Treasure Boxes (atcoder.jp)

【题目描述】

After a long adventure, Takahashi has reached ruins containing \(N\) treasure chests.
经过漫长的冒险,高桥抵达了藏有 \(N\) 个宝箱的遗迹。

Each treasure chest \(i\) (\(1 \leq i \leq N\)) is secured with a lock of strength \(C_i\). A larger strength value means a sturdier lock. All treasure chests have mutually distinct lock strengths.
每个宝箱 \(i\)\(1 ≤ i ≤ N\))都配有一把强度为 \(C_i\) 的锁。强度值越大,意味着锁越坚固。所有宝箱的锁强度互不相同。

Takahashi has \(M\) keys. Each key \(j\) (\(1 \leq j \leq M\)) has an unlocking power \(R_j\). Key \(j\) can open treasure chest \(i\) only if the key's unlocking power is at least as large as the lock's strength, that is, when \(C_i \leq R_j\). Note that different keys may have the same unlocking power.
高桥拥有 \(M\) 把钥匙。每把钥匙 \(j\)\(1 ≤ j ≤ M\))具有开启能力 \(R_j\)。钥匙 \(j\) 仅当开启能力至少与锁的强度一样大时才能打开宝箱 \(i\),即当 \(C_i ≤ R_j\) 时。注意,不同的钥匙可能具有相同的开启能力。

Takahashi selects some treasure chests he wants to open (possibly \(0\)), and assigns exactly one key to each selected treasure chest to open it. However, the same key cannot be assigned to two or more treasure chests. Additionally, the key assigned to each treasure chest must be able to open it (\(C_i \leq R_j\)). It is fine to leave some keys unused or some treasure chests unopened.
高桥选择一些他想要打开的宝箱(可能为 \(0\) 个),并为每个被选中的宝箱分配恰好一把钥匙来打开它。然而,同一把钥匙不能被分配给两个或更多宝箱。此外,分配给每个宝箱的钥匙必须能够打开它(\(C_i ≤ R_j\))。可以留一些钥匙不使用,也可以留一些宝箱不打开。

Find the maximum number of treasure chests Takahashi can open.
求高桥能够打开的最大宝箱数量。

【输入】

\(N\) \(M\)
\(C_1\) \(C_2\) \(\ldots\) \(C_N\)
\(R_1\) \(R_2\) \(\ldots\) \(R_M\)

  • The first line contains an integer \(N\) representing the number of treasure chests and an integer \(M\) representing the number of keys, separated by a space.
  • The second line contains integers \(C_1, C_2, \ldots, C_N\) representing the lock strengths of the treasure chests, separated by spaces.
  • The third line contains integers \(R_1, R_2, \ldots, R_M\) representing the unlocking powers of the keys, separated by spaces.

【输出】

Print the maximum number of treasure chests Takahashi can open, on a single line.

【输入样例】

3 3
10 20 30
15 25 35

【输出样例】

3

【核心思想】

  1. 问题分析:给定 \(N\) 个宝箱,每个宝箱有锁强度 \(C_i\);以及 \(M\) 把钥匙,每把钥匙有开启能力 \(R_j\)。钥匙 \(j\) 能打开宝箱 \(i\) 当且仅当 \(R_j \geq C_i\)。每把钥匙最多使用一次,每个宝箱最多被打开一次。目标是最大化能打开的宝箱数量。这是一个双指针贪心问题,关键在于如何最优地匹配钥匙和宝箱。

  2. 算法选择

    • 排序 + 双指针贪心:先将宝箱锁强度和钥匙开启能力分别升序排序,然后用双指针进行贪心匹配
    • 贪心策略:优先用能力最小的钥匙去开能打开的最容易开的宝箱,保留更强的钥匙给更难开的宝箱
  3. 关键步骤

    • 排序:将数组 \(C[1..N]\)(宝箱锁强度)和 \(R[1..M]\)(钥匙开启能力)分别升序排序
    • 初始化:指针 \(i = 1\)(指向当前宝箱),指针 \(j = 1\)(指向当前钥匙),计数器 \(cnt = 0\)
    • 双指针遍历(当 \(i \leq N\)\(j \leq M\) 时):
      • 跳过无法匹配的钥匙:当 \(R[j] < C[i]\) 时,钥匙 \(j\) 无法打开宝箱 \(i\)\(j\) 右移
      • 匹配成功:如果 \(j \leq M\)(找到能用的钥匙),则 \(i++\)\(j++\)\(cnt++\)
      • 无可用钥匙:如果 \(j > M\),当前宝箱无法打开,\(i++\)
    • 输出最大可打开宝箱数 \(cnt\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N + M \log M)\),主要是排序的复杂度
    • 空间复杂度:\(O(N + M)\),存储宝箱和钥匙信息
  5. 双指针贪心的核心思想

    • 排序预处理:将两个序列排序后,问题转化为在有序序列中寻找最优匹配
    • 贪心正确性:优先匹配最容易开的宝箱,使用能满足条件的最弱钥匙,这样可以保留更强的钥匙给更难开的宝箱
    • 单调性:由于两个序列都升序排列,如果当前钥匙无法打开当前宝箱,那么它也无法打开后续任何宝箱
    • 最优性证明:这种贪心策略能得到最大匹配数,因为每个宝箱都使用了能满足条件的最小钥匙,不会浪费强钥匙
    • 适用于两类物品的匹配问题,如优惠券分配、资源分配等场景

【解题思路】

【算法标签】

双指针

【代码详解】

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

const int N = 200005;
int n, m;  // n: 商品数量,m: 优惠券数量
int c[N], r[N];  // c: 商品价格数组,r: 优惠券金额数组

int main()
{
    cin >> n >> m;  // 读入商品数量和优惠券数量

    for (int i = 1; i <= n; i++)
    {
        cin >> c[i];  // 读入第i个商品的价格
    }
    for (int i = 1; i <= m; i++)
    {
        cin >> r[i];  // 读入第i张优惠券的金额
    }

    // 对商品价格进行升序排序,从下标1到n
    sort(c + 1, c + n + 1);
    // 对优惠券金额进行升序排序,从下标1到m
    sort(r + 1, r + m + 1);

    int cnt = 0;  // 计数器,记录有多少商品可以使用优惠券
    // 双指针贪心匹配
    int i = 1, j = 1;  // i: 商品指针,指向当前要处理的商品;j: 优惠券指针,指向当前要使用的优惠券

    // 当还有商品未处理且还有优惠券可用时,继续匹配
    while (i <= n && j <= m)
    {
        // 跳过所有金额小于当前商品价格的优惠券
        // 因为优惠券金额必须大于等于商品价格才能使用
        while (j <= m && r[j] < c[i])
        {
            j++;  // 当前优惠券金额太小,尝试下一张优惠券
        }

        // 如果还有可用的优惠券(即找到了金额>=商品价格的优惠券)
        if (j <= m)
        {
            i++;  // 处理下一个商品
            j++;  // 使用当前优惠券,并指向下一张优惠券
            cnt++;  // 成功匹配一个商品,计数器加1
        }
        else  // 如果没有可用的优惠券了(所有优惠券金额都小于当前商品价格)
        {
            i++;  // 当前商品无法使用任何优惠券,处理下一个商品
        }
    }

    cout << cnt << endl;  // 输出能使用优惠券的商品数量
    return 0;
}

【运行结果】

3 3
10 20 30
15 25 35
3
posted @ 2026-06-14 10:37  团爸讲算法  阅读(14)  评论(0)    收藏  举报