$("head").append('')

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;//好习惯
}
posted @ 2025-10-12 15:33  lain_yc  阅读(2)  评论(0)    收藏  举报
$("head").append('')