专栏文章
题解:P4952 [USACO04MAR] Financial Aid
P4952题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miozk3s7
- 此快照首次捕获于
- 2025/12/03 03:43 3 个月前
- 此快照最后确认于
- 2025/12/03 03:43 3 个月前
题目大意
贝西需要从C头奶牛中选出N头(N为奇数)进入哞哞大学,要求:
- 中位数最大化:选出的N头奶牛的CSAT成绩的中位数尽可能大。
- 奖学金限制:这些奶牛申请的奖学金总额不能超过F。
如果无法满足条件,输出
-1。解题思路
关键观察
- 中位数的性质:
- 中位数是排序后位于中间的数(因为N是奇数)。
- 要使中位数尽可能大,应该尽量选择CSAT成绩高的奶牛。
- 奖学金限制:
- 总奖学金不能超过F,因此需要在保证中位数最大的前提下,尽量选择奖学金少的奶牛。
算法选择
由于直接枚举所有可能的组合计算量太大(C可能达到1e5),我们需要一个高效的算法:
- 排序:按CSAT成绩从高到低排序,方便后续处理。
- 预处理:
- 计算前i头奶牛中,最小的
k = (N + 1) / 2 - 1个奖学金的和(left[i])。 - 计算后i头奶牛中,最小的
k个奖学金的和(right[i])。
- 计算前i头奶牛中,最小的
- 验证中位数:
- 遍历每头奶牛,假设它是中位数,检查是否能选出
N头奶牛满足:- 左边至少有
k头成绩 ≥ 中位数,右边至少有k头成绩 ≤ 中位数。 - 总奖学金
= 中位数奶牛的奖学金 + left[i-1] + right[i+1] ≤ F。
- 左边至少有
- 遍历每头奶牛,假设它是中位数,检查是否能选出
优化方法
- 优先队列(最大堆):用于快速计算前
k小的奖学金和。 - 提前终止:一旦找到满足条件的最大中位数,立即输出结果,减少计算量。
AC代码
CPP#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
struct Cow {
int score;
int aid;
};
bool compare(const Cow &a, const Cow &b) {
return a.score > b.score; // 按成绩降序排序
}
int main() {
int N, C, F;
cin >> N >> C >> F;
vector<Cow> cows(C);
for (int i = 0; i < C; ++i) {
cin >> cows[i].score >> cows[i].aid;
}
sort(cows.begin(), cows.end(), compare); // 排序
int k = (N + 1) / 2;
vector<int> left(C, -1); // left[i] 表示前i头奶牛中最小的k-1个奖学金和
vector<int> right(C, -1); // right[i] 表示后i头奶牛中最小的k-1个奖学金和
// 预处理左边的最小奖学金和
priority_queue<int> max_heap; // 最大堆(堆顶是最大的数)
int sum = 0;
for (int i = 0; i < C; ++i) {
max_heap.push(cows[i].aid);
sum += cows[i].aid;
if (max_heap.size() > k - 1) {
sum -= max_heap.top(); // 移除最大的,保留最小的k-1个
max_heap.pop();
}
if (max_heap.size() == k - 1) {
left[i] = sum;
}
}
// 预处理右边的最小奖学金和
while (!max_heap.empty()) max_heap.pop();
sum = 0;
for (int i = C - 1; i >= 0; --i) {
max_heap.push(cows[i].aid);
sum += cows[i].aid;
if (max_heap.size() > k - 1) {
sum -= max_heap.top();
max_heap.pop();
}
if (max_heap.size() == k - 1) {
right[i] = sum;
}
}
// 遍历寻找最大的中位数
int max_median = -1;
for (int i = k - 1; i <= C - k; ++i) {
if (left[i - 1] != -1 && right[i + 1] != -1) {
int total = cows[i].aid + left[i - 1] + right[i + 1];
if (total <= F) {
max_median = cows[i].score;
break; // 找到最大的中位数,直接退出
}
}
}
cout << max_median << endl;
return 0;
}
复杂度分析
- 时间复杂度:
- 排序:
O(C log C)。 - 预处理
left和right:O(C log k)(优先队列操作)。 - 遍历验证中位数:
O(C)。 - 总时间复杂度:
O(C log C),适用于C ≤ 1e5。
- 排序:
- 空间复杂度:
O(C)(存储奶牛和预处理数组)。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...