题解:洛谷 P13013 奖品兑换

【题目来源】

洛谷:P13013 [GESP202506 五级] 奖品兑换 - 洛谷

【题目描述】

班主任给上课专心听讲、认真完成作业的同学们分别发放了若干张课堂优秀券和作业优秀券。同学们可以使用这两种券找班主任兑换奖品。具体来说,可以使用 \(a\) 张课堂优秀券和 \(b\) 张作业优秀券兑换一份奖品,或者使用 \(b\) 张课堂优秀券和 \(a\) 张作业优秀券兑换一份奖品。

现在小 A 有 \(n\) 张课堂优秀券和 \(m\) 张作业优秀券,他最多能兑换多少份奖品呢?

【输入】

第一行,两个正整数 \(n,m\),分别表示小 A 持有的课堂优秀券和作业优秀券的数量。

第二行,两个正整数 \(a,b\),表示兑换一份奖品所需的两种券的数量。

【输出】

输出共一行,一个整数,表示最多能兑换的奖品份数。

【输入样例】

8 8
2 1

【输出样例】

5

【核心思想】

  1. 问题分析:给定课堂优秀券 \(n\) 张、作业优秀券 \(m\) 张,兑换一份奖品需要 \((a, b)\)\((b, a)\) 的组合。求最多能兑换的奖品份数。这是一个整数二分 + 可行性判定问题,核心在于对答案进行二分,并验证给定兑换数量是否可行。

  2. 算法选择

    • 整数二分:在 \([0, \min(n,m)]\) 范围内二分答案,寻找最大可行的兑换数量
    • 贪心调整策略:假设兑换 \(mid\) 份,优先按 \((a, b)\) 方式分配,若资源不足则通过交换调整为 \((b, a)\) 方式
  3. 关键步骤

    • 预处理:确保 \(n \geq m\)\(a \geq b\)(通过 \(swap\) 统一处理)
    • 二分答案\(l = 0, r = m\)):
      • \(mid = (l + r) / 2\)
      • \(check(mid)\) 可行:\(ans \leftarrow mid, l \leftarrow mid + 1\)
      • 否则:\(r \leftarrow mid - 1\)
    • 可行性判定 \(check(mid)\)
      • 假设全部按 \((a, b)\) 分配:\(x = a \cdot mid, y = b \cdot mid\)
      • \(x > n\)(第一种资源不足):需要将部分兑换从 \((a, b)\) 改为 \((b, a)\),每次调整减少 \(a-b\) 的第一种资源、增加 \(a-b\) 的第二种资源
      • 调整次数 \(k = \lceil (x - n) / (a - b) \rceil\),更新 \(x \leftarrow x - k(a-b), y \leftarrow y + k(a-b)\)
      • 返回 \(x \leq n\)\(y \leq m\)
    • 输出结果\(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(\log(\min(n,m)))\),二分次数为对数级别,每次 \(check\)\(O(1)\)
    • 空间复杂度:\(O(1)\),仅使用常数额外空间
  5. 整数二分与可行性判定的核心思想

    • 单调性利用:若兑换 \(mid\) 份可行,则兑换少于 \(mid\) 份也一定可行,答案具有单调性,适合二分
    • 贪心调整原理:优先按"消耗更多第一种资源"的方式分配,若第一种资源不足则通过交换调整为"消耗更少第一种资源"的方式。每次交换减少 \(a-b\) 的第一种资源消耗,同时增加 \(a-b\) 的第二种资源消耗
    • 向上取整的边界处理\(k = \lceil (x-n)/(a-b) \rceil\) 确保调整后 \(x \leq n\),使用浮点计算或整数技巧实现
    • 对称性统一:通过 \(swap\) 确保 \(n \geq m\)\(a \geq b\),简化后续逻辑,避免分类讨论
    • 适用于资源分配、组合优化中答案具有单调性的二分判定类问题

【算法标签】

普及 #整数二分

【代码详解】

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

#define int long long  // 定义int为long long类型
const int N = 10005;   // 定义最大可能值(未实际使用)

int n, m;              // 两个资源总量
int a, b;              // 两种资源的消耗比例
int ans = 0;           // 存储最终结果

// 检查在给定mid值下是否满足资源约束
bool check(int mid)
{
    // 计算按比例消耗的资源量
    int x = a * mid;  // 第一种资源消耗量
    int y = b * mid;  // 第二种资源消耗量

    // 如果第一种资源超限,调整消耗量
    if (x > n)
    {
        // 计算需要调整的次数
        int k = ceil(1.0 * (x - n) / (a - b));
        // 调整后的资源消耗量
        x -= k * (a - b);
        y += k * (a - b);
    }

    // 检查调整后的资源是否都在限制范围内
    return x <= n && y <= m;
}

signed main()
{
    // 输入资源总量和消耗比例
    cin >> n >> m;
    cin >> a >> b;

    // 确保n >= m且a >= b(优化比较)
    if (n < m) swap(n, m);
    if (a < b) swap(a, b);

    // 二分查找最大满足条件的mid值
    int l = 0, r = m;  // 初始化二分范围
    while (l <= r)
    {
        int mid = (l + r) / 2;  // 计算中间值

        // 检查当前mid是否满足条件
        if (check(mid))
        {
            ans = mid;      // 更新答案
            l = mid + 1;    // 尝试更大的值
        }
        else
            r = mid - 1;    // 尝试更小的值
    }

    // 输出最终结果
    cout << ans << endl;

    return 0;
}

【运行结果】

8 8
2 1
5
posted @ 2026-07-16 15:38  团爸讲算法  阅读(5)  评论(0)    收藏  举报