题解:洛谷 P14635 糖果店

【题目来源】

洛谷:P14635 [NOIP2025] 糖果店 - 洛谷

【题目描述】

小 X 开了一家糖果店,售卖 \(n\) 种糖果,每种糖果均有无限颗。对于不同种类的糖果,小 X 采用了不同的促销策略。具体地,对于第 \(i\) (\(1 \le i \le n\))种糖果,购买第一颗的价格为 \(x_i\) 元,第二颗为 \(y_i\) 元,第三颗又变回 \(x_i\) 元,第四颗则为 \(y_i\) 元,以此类推。

小 R 带了 \(m\) 元钱买糖果。小 R 不关心糖果的种类,只想得到数量尽可能多的糖果。你需要帮助小 R 求出,\(m\) 元钱能购买的糖果数量的最大值。

【输入】

输入的第一行包含两个正整数 \(n, m\),代表糖果的种类数和小 R 的钱数。

输入的第 \(i+1\) (\(1 \le i \le n\))行包含两个正整数 \(x_i, y_i\),分别表示购买第 \(i\) 种糖果时第奇数颗的价格和第偶数颗的价格。

【输出】

输出一行一个非负整数,表示 \(m\) 元钱能购买的糖果数量的最大值。

【输入样例】

2 10
4 1
3 3

【输出样例】

4

【核心思想】

  1. 问题分析:给定 \(n\) 种糖果,每种有无限颗,价格交替为 \(x_i\)(奇数颗)和 \(y_i\)(偶数颗)。带 \(m\) 元钱,求最多能买多少颗。这是一个反悔贪心问题,关键在于:每两颗糖果(一奇一偶)构成一个"购买周期",成本为 \(x_i + y_i\)。最优策略是先按某种规则买第一颗,然后用最便宜的周期反复购买。

  2. 算法选择

    • 周期成本分析:每种糖果的两颗周期成本为 \(S_i = x_i + y_i\),找到最小周期成本 minS = min(S_i)
    • 排序 + 枚举:按 \(x_i\) 升序排序,枚举购买前 \(i\) 种糖果的第一颗(花费 \(\sum_{j=1}^i x_j\)),剩余钱用 minS 周期反复购买
    • 反悔机制:排序后枚举所有可能的"首次购买组合",取最大值
  3. 关键步骤

    • 读入数据\(n, m\) 和每种糖果的 \(x_i, y_i\),计算 minS = min(x_i + y_i)
    • 排序:按 \(x_i\) 升序排序
    • 枚举\(i\)\(0\)\(n\)):
      • sum += a[i].x(累计前 \(i\) 种糖果的第一颗花费)
      • m < sum,跳过(预算不足)
      • ans = max(ans, (m - sum) / minS * 2 + i)
        • (m - sum) / minS:剩余预算可购买的完整周期数
        • * 2:每个周期2颗糖
        • + i:已购买的 \(i\) 颗第一颗糖
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),枚举 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储糖果信息
  5. 反悔贪心 + 周期分析的核心思想

    • 周期购买的最优性:买偶数颗糖时,必然成对购买(奇+偶)最划算,因为单独买偶数颗没有对应的奇数颗前提是不可能的
    • 最小周期成本的复用:找到所有糖果中 \(x_i + y_i\) 最小的,用剩余预算尽可能多地购买该周期,这是贪心最优的
    • 首次购买的枚举:排序后枚举前 \(i\) 种糖果各买第一颗,因为 \(x_i\) 小的优先买第一颗更可能使总数量最大化
    • 反悔的本质:不预先确定买哪些糖果的第一颗,而是枚举所有可能,用全局最优覆盖局部贪心
    • 适用于"交替价格 + 无限购买"的最优化问题,核心在于周期成本分析和枚举反悔

【算法标签】

普及 #反悔贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;
int n, m, ans;  // n: 物品数量,m: 总预算,ans: 最大价值
struct Node
{
    int x, y;  // x: 购买价格,y: 回收价格
} a[N];

bool cmp(Node x, Node y)  // 排序比较函数,按购买价格升序排序
{
    return x.x < y.x;
}

signed main()
{
    cin >> n >> m;  // 输入物品数量和总预算
    int minS = 1e18;  // 初始化最小成本为无穷大
    for (int i = 1; i <= n; i++)  // 输入每个物品的信息
    {
        int x, y;
        cin >> a[i].x >> a[i].y;  // 输入购买价格和回收价格
        minS = min(minS, a[i].x + a[i].y);  // 更新最小成本(购买+回收的价格)
    }

    sort(a + 1, a + n + 1, cmp);  // 按购买价格升序排序
    int sum = 0;  // 已花费的总和
    for (int i = 0; i <= n; i++)  // 遍历前i个物品,i从0到n
    {
        sum += a[i].x;  // 累计购买价格
        if (m < sum)  // 如果总预算不够购买前i个物品
            continue;  // 跳过当前i

        // 计算当前方案的最大价值
        // 先购买前i个物品,剩余的钱可以多次购买回收最便宜的商品
        // (m - sum)/minS * 2: 剩余预算可购买的次数 * 2(买入卖出一次算2次操作)
        // + i: 已经购买的物品数量
        ans = max(ans, (m - sum) / minS * 2 + i);
    }
    cout << ans << endl;  // 输出最大价值
    return 0;
}

【运行结果】

2 10
4 1
3 3
4
posted @ 2026-08-10 12:26  团爸讲算法  阅读(22)  评论(0)    收藏  举报