bzoj5185 [Usaco2018 Jan]Lifeguards

农夫约翰为奶牛开设游泳池并雇佣救生员,但超出了预算。需要解雇K名救生员,求解剩余救生员能覆盖的最大时间长度。通过贪心算法和单调队列优化求解。
摘要由CSDN通过智能技术生成

http://www.elijahqi.win/archives/2987
Description
农夫约翰为他的奶牛们开了一个游泳池,他认为这将有助于他们放松和生产更多的牛奶。为确保安全,他请了N只
牛做救生员,每只牛都有一个工作时间,为一些连续的时间间隔为了简单起见,泳池每天从时间0打开到到10^9,
所以每个区间都可以用两个整数来描述,给定的这两个整数就是区间的开始和结束时刻。例如,一个救生员从t =
4时开始工作和在t=7时结束,共覆盖三个时间单位(注意端点也是覆盖到的时间点)。但不幸的是,农夫约翰雇佣
了比它支付能力多出K个的救生员。他需要开除正好K个救生员,求出剩余的救生员最大能够覆盖多长的时间(一段
时间被覆盖当且仅当这时有至少一个救生员在工作)
Input
第一行包含N和K(K≤N≤10^5,1≤K≤100)
接下来的N行,每行描述一个在区间1…10^9上的救生员
给定这个救生员区间的开始和结束位置,其中有些救生员的区间可能会相交。
Output
请输出一个数字,表示如果农夫约翰开除K个救生员,剩余救生员最大能够覆盖的时间长度
Sample Input
3 2
1 8
7 15
2 14
Sample Output
12
农夫约翰应该开除掉覆盖1…8和7…15两个区间的两个救生员
HINT
Source
Platinum 鸣谢WenDavid提供翻译
首先贪心的把包含于其他线段的线段删除 然后按照左端点排序 那么显然右端点也是单调递增的
设dp[i][k]表示 前i条线段删除k条 i这条线段必须选的最大覆盖长度是多少
那么dp[i][k]=max{dp[j][k-(i-j-1)]};
那么显然可以知道我加入由dp[x][y]->dp[i][j] 那么显然我需要满足 x-y=i-j-1
那么如果i与j 不相交的时候 我可以储存我这个dp的最大值 然后直接加上len[i]即可 如果相交的话 我可以设dp[i][j]+line[i].r-line[x].l所以我针对我后面这个维护多个单调队列即可
然后最后统计答案的时候相当于 我枚举我最后一条边选取的是哪些边 这样的话恰好保证答案的不重不漏

#include<deque>
好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值