题解:洛谷 P13013 奖品兑换
【题目来源】
洛谷:P13013 [GESP202506 五级] 奖品兑换 - 洛谷
【题目描述】
班主任给上课专心听讲、认真完成作业的同学们分别发放了若干张课堂优秀券和作业优秀券。同学们可以使用这两种券找班主任兑换奖品。具体来说,可以使用 \(a\) 张课堂优秀券和 \(b\) 张作业优秀券兑换一份奖品,或者使用 \(b\) 张课堂优秀券和 \(a\) 张作业优秀券兑换一份奖品。
现在小 A 有 \(n\) 张课堂优秀券和 \(m\) 张作业优秀券,他最多能兑换多少份奖品呢?
【输入】
第一行,两个正整数 \(n,m\),分别表示小 A 持有的课堂优秀券和作业优秀券的数量。
第二行,两个正整数 \(a,b\),表示兑换一份奖品所需的两种券的数量。
【输出】
输出共一行,一个整数,表示最多能兑换的奖品份数。
【输入样例】
8 8
2 1
【输出样例】
5
【核心思想】
-
问题分析:给定课堂优秀券 \(n\) 张、作业优秀券 \(m\) 张,兑换一份奖品需要 \((a, b)\) 或 \((b, a)\) 的组合。求最多能兑换的奖品份数。这是一个整数二分 + 可行性判定问题,核心在于对答案进行二分,并验证给定兑换数量是否可行。
-
算法选择:
- 整数二分:在 \([0, \min(n,m)]\) 范围内二分答案,寻找最大可行的兑换数量
- 贪心调整策略:假设兑换 \(mid\) 份,优先按 \((a, b)\) 方式分配,若资源不足则通过交换调整为 \((b, a)\) 方式
-
关键步骤:
- 预处理:确保 \(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(\log(\min(n,m)))\),二分次数为对数级别,每次 \(check\) 为 \(O(1)\)
- 空间复杂度:\(O(1)\),仅使用常数额外空间
-
整数二分与可行性判定的核心思想:
- 单调性利用:若兑换 \(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
浙公网安备 33010602011771号