✏️ 纠错
第 132 题 / 共 155 题

题目背景

为了保证只有时间复杂度正确的代码能够通过本题,时限下降为 400 毫秒。

题目描述

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

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

输入格式

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

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

输出格式

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

输入输出样例

输入 #1

8 8
2 1

输出 #1

5

输入 #2

314159 2653589
27 1828

输出 #2

1599

说明/提示

对于 60% 的测试点,保证 1≤a,b≤100,1≤n,m≤500。

对于所有测试点,保证 1≤a,b≤104,1≤n,m≤109。

📝 题目解析

【解析】

1.核心问题:两种兑换方式可混合使用,需找到最大份数v使得总消耗不超过n 和 m。

2.关键观察:

设用第一种方式(a,b)x次,第二种方式(b,a)y次,则x+y=v。

总消耗:ax+by≤ n且bx+ay≤ m。

化简得:av+(b-a)y≤ n 且av+(b-a)x≤ m。

3.算法选择:

二分查找最大v(范围0 到min(n/max(a,b),m/max(a,b)))。

对每个候选v,检查是否存在合法的x,y组合(通过不等式推导验证)。

参考程序
#include <cstdio>
#include <algorithm>
using namespace std;
int n, m, a, b;
int l, r;
int check(int v) {
    long long x, y, t;
    x = 1ll * v * a;
    y = 1ll * v * b;
    if (y > m) {
        t = (y - m + (b - a) - 1) / (b - a);
        y -= t * (b - a);
        x += t * (b - a);
    }
    return x <= n && y <= m;
}
int main() {
    scanf("%d%d", &n, &m);
    scanf("%d%d", &a, &b);
    if (n > m)
        swap(n, m);
    if (a > b)
        swap(a, b);
    if (a == b) {
        printf("%d\n", n / a);
        return 0;
    }
    l = 0;
    r = n;
    while (l < r) {
        int mid = (l + r + 1) >> 1;
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }
    printf("%d\n", r);
    return 0;
}