P14097 [POCamp 2022] 救火 / Brinnande träd
P14097 [POCamp 2022] 救火 / Brinnande träd
题目描述
一场森林火灾在一个自然保护区爆发。该区域有一种稀有的树种,世界其他地方不存在。你身处森林中,在回家的路上,你打算尽可能拯救更多的树。
总共有 $ N $ 棵稀有树,编号从 1 到 $ N $,你将按这个顺序从它们身边跑过。拯救第 $ i $ 棵树需要 $ A_i $ 秒,但你必须最迟在第 $ X_i $ 秒开始拯救,否则这棵树会被烧毁。另一方面,即使第 $ X_i $ 秒落在你正在拯救该树的过程中也没有关系。
在树与树之间奔跑对你来说不花任何时间,但你只能向前跑,因此只能按你遇到它们的顺序救树。另外,你已知 $ X_1 \le X_2 \le X_3, \dots \le X_N $。
求你最多能救下多少棵树。
输入格式
第一行包含一个整数 $ N \((\) 1 \le N \le 4 \cdot 10^5 $),表示树的数量。
第二行包含 $ N $ 个整数 $ X_i \((\) 0 \le X_i \le 10^9 $),表示你必须开始拯救第 $ i $ 棵树的最晚秒数。
第三行包含 $ N $ 个整数 $ A_i \((\) 1 \le A_i \le 10^9 $),表示拯救第 $ i $ 棵树所需的时间。
输出格式
输出一个整数,为可以拯救的树的最大数量。
输入输出样例 #1
输入 #1
6
1 1 2 2 5 8
2 3 3 5 2 2
输出 #1
4
输入输出样例 #2
输入 #2
5
0 0 1 2 3
5 1 1 1 1
输出 #2
4
输入输出样例 #3
输入 #3
3
5 5 5
6 1 1
输出 #3
2
说明/提示
子任务
本题采用捆绑测试。
| 子任务编号 | 得分 | 限制 |
|---|---|---|
| 1 | 10 | $ A_1 = A_2 = \dots = A_N $ |
| 2 | 18 | $ X_1 = X_2 = \dots = X_N $ |
| 3 | 15 | $ N, A_i, X_i \le 100 $ |
| 4 | 22 | $ N \le 2000 $ |
| 5 | 35 | 无额外限制 |
题解:
说句闲话:看到这个比赛难度的时候我差点被唬住了,不过没想到 \(T_1\) 这么水 \(()\)。
一.题意
存在 \(n\) 棵树,每棵树需要在 \(X_i\) 之前被救助 \((\) 注意这里!不是结束时间! \()\) 即每棵树的存活时间是 \([0 \sim X_i]\)。特别地,\(X\) 是递增的,不用再排序了。
同时,救助第 \(i\) 棵树需要花费的时间是 \(A_i\),想让你求出最多能救助多少棵树?
二.思路
刚拿到这个题看了一眼感觉是 \(DP\),但是仔细一看发现没有那个必要,然后想到了反悔贪心。
那,什么是反悔贪心呢?
顾名思义,就是可以反悔的贪心是指:
思路是无论当前的选项是否最优都接受,然后进行比较,如果选择之后不是最优了,则反悔,舍弃掉这个选项;否则,正式接受。如此往复。
对于这个题,就是如果救了一棵树,但是发现下一颗树和这棵树只能救一个,并且比当前总时间更短,那么我们就选择杀死这棵树(即反悔,不救了),去救下一棵更快的树。
此时,不难发现需要替换掉最劣的树,也就是说只要一个时间的最大值,此时我们可以使用大根堆来维护救的树中花费最高的。
此时,如果有一个花费更少的树,在你只能选择救堆顶的树或者当前的树时,救当前的这棵树就可以使得总时间花费更短。那么,我们救助的总数没变,只有时间缩短了,这无疑是我们想要的。
实现如下:
if(!s.empty()&&a[i].lst<now){//如果救了当前的树没法接着救了再反悔,否则会亏数量
//注意当前没有树的情况,那样谈何最劣呢?
if(a[i].tim<s.top()){//当面前的树比你救治的花费最大的树更优就替换掉它
now-=s.top();
s.pop();
s.push(a[i].tim);
now+=a[i].tim;
//因为舍弃了一棵树又救了一棵树,总数不变
}
}
如果时间够你救下更多的树,就不要舍弃它们啦:
else{//对应上面最外层if
now+=a[i].tim;
ans++;
s.push(a[i].tim);
}
本题主体部分就是这样,还有一个坑点就是 \(X_i\) 是开始救治的最晚时间而不是救治结束的最晚时间。
三.AC代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int lst,tim;
}a[(int)(4e5+5)];
long long n,ans,now,cnt;
priority_queue<int> s;
int main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].lst;
for(int i=1;i<=n;i++)cin>>a[i].tim;
for(int i=1;i<=n;i++){
if(!s.empty()&&a[i].lst<now){
if(a[i].tim<s.top()){
now-=s.top();
s.pop();
s.push(a[i].tim);
now+=a[i].tim;
}
}
else{
now+=a[i].tim;
ans++;
s.push(a[i].tim);
}
}
cout<<ans;
return 0;//好习惯
}

浙公网安备 33010602011771号