题解:洛谷 P14635 糖果店
【题目来源】
【题目描述】
小 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
【核心思想】
-
问题分析:给定 \(n\) 种糖果,每种有无限颗,价格交替为 \(x_i\)(奇数颗)和 \(y_i\)(偶数颗)。带 \(m\) 元钱,求最多能买多少颗。这是一个反悔贪心问题,关键在于:每两颗糖果(一奇一偶)构成一个"购买周期",成本为 \(x_i + y_i\)。最优策略是先按某种规则买第一颗,然后用最便宜的周期反复购买。
-
算法选择:
- 周期成本分析:每种糖果的两颗周期成本为 \(S_i = x_i + y_i\),找到最小周期成本
minS = min(S_i) - 排序 + 枚举:按 \(x_i\) 升序排序,枚举购买前 \(i\) 种糖果的第一颗(花费 \(\sum_{j=1}^i x_j\)),剩余钱用
minS周期反复购买 - 反悔机制:排序后枚举所有可能的"首次购买组合",取最大值
- 周期成本分析:每种糖果的两颗周期成本为 \(S_i = x_i + y_i\),找到最小周期成本
-
关键步骤:
- 读入数据:\(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
- 读入数据:\(n, m\) 和每种糖果的 \(x_i, y_i\),计算
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),枚举 \(O(n)\)
- 空间复杂度:\(O(n)\),存储糖果信息
-
反悔贪心 + 周期分析的核心思想:
- 周期购买的最优性:买偶数颗糖时,必然成对购买(奇+偶)最划算,因为单独买偶数颗没有对应的奇数颗前提是不可能的
- 最小周期成本的复用:找到所有糖果中 \(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
浙公网安备 33010602011771号