洛谷 建筑抢修
题目思路:贪心 + 堆
题目:
思路解析:取自洛谷[Gypsophila ] 的解析
贪心策略:
- 直接按 t 贪心?显然不行。
- 那我们考虑先按 t贪心,中途再更改。
- 按 t 从小到大排序之后,开始轮流遍历每个建筑。
- 如果中途某个建筑 i 无法在 ti的时间内修复,那么在先前选择修复的建筑中(放在了堆中)拿出 aj 最大的 j 号建筑。若 ai<aj,则放弃 j 转而修 i。(主思路)
策略证明:
- 若第 i号出现时间不足,那么前 i 个建筑中最多修复 i−1个建筑
- 则我们必然选择 ai 较小的前 i−1 个建筑,给后面的修复留下更多的时间
代码:
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 15e4+5;
int n, Time, ans;
priority_queue<int> q;
//定义每个建筑的结构体
typedef struct{
int w, t;
}Build;
Build build[N];
//定制排序规则
bool cmp ( Build a, Build b )
{
return a.t < b.t;
}
int main()
{
cin >> n;
for ( int i = 1; i <= n; ++i ) {
cin >> build[i].w >> build[i].t;
}
//按维修时间t从小到大排序
sort(build+1, build+1+n, cmp);
for ( int i = 1; i <= n; ++i ) {
//当前建筑不能在时间内修好
/*
实质上就是,当第i号建筑不能按时修好的时候,我们来考虑第i号和堆中
耗时最大的谁更好,
*/
//当前第i号耗时比top()少,就修它了
if ( Time + build[i].w <= build[i].t ) {
ans++;
q.push(build[i].w);
Time += build[i].w;
}
//能修好,放入堆中
else {
if ( build[i].w < q.top() ) {
Time -= q.top();
Time += build[i].w;
q.pop();
q.push(build[i].w);
}
}
}
cout << ans << endl;
return 0;
}
下面有一个类似的题目:(贪心 + 堆)
来自于pta(IT协会出的题目)
在一条直线上从左至右有 n 个点,分别为 1、2、3、…、n。每一个点都有一瓶魔法饮料,每瓶魔法饮料会提供 ai 点魔法值。其中,ai 可能为负数,意味着它有可能会减你的魔法值。
珂朵莉要从 1 走到 n,她的初始魔法值为 0。当她走到 i 这个点时,她可以选择喝这瓶魔法饮料或者不喝。注意,只能从左走到右,即依次经过 1、2、3、…、n,且不能回退。
在这个过程中,珂朵莉需要始终保持自己的魔法值为非负数。
请你求出珂朵莉最多能喝多少瓶魔法饮料。
输入格式:
第一行为一个整数 n(1≤n≤200000) ,为 n 个点,即 n 瓶魔法饮料。
第二行为 n 个整数,分别为 a1,a2,…,an(−109≤ai≤109) ,意味着每瓶魔法饮料对应的魔法值。
输出格式:
输出一个整数,即在保持魔法值为非负数的情况下,珂朵莉最多能喝多少瓶魔法饮料。
输入样例:
在这里给出一组输入。例如:
6 4 -4 1 -3 1 -3
输出样例:
在这里给出相应的输出。例如:
5
样例解释:
对于样例,珂朵莉可以喝第 1,3,4,5,6,这五瓶饮料。
读一个数就把这个数放进最小堆里面去,并且加上,如果当前sum小于了0,然后就不断地减去最小堆的top(),就表示当前top()的饮料不喝,然后优先队列pop(),直到当前值非负位置。可以想到,pop的都是负数!!!
#include <iostream>
#include <queue>
#define ll long long
using namespace std;
ll sum;
//最小堆
priority_queue<ll, vector<ll>, greater<ll> > q;
int main()
{
int n;
cin >> n;
for ( int i = 1; i <= n; i++){
ll x;
cin >> x;
q.push(x);
sum += x;
while ( sum < 0 ){
sum -= q.top();
q.pop();
}
}
cout << q.size();
return 0;
}