洛谷 P2627 修剪草坪 题解
P2627 修剪草坪
题目描述
在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠。
然而,Farm John的草坪非常脏乱,因此,Farm John只能够让他的奶牛来完成这项工作。Farm John有N(1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,奶牛i的效率为E_i(0 <= E_i <= 1,000,000,000)。
靠近的奶牛们很熟悉,因此,如果Farm John安排超过K只连续的奶牛,那么,这些奶牛就会罢工去开派对:)。因此,现在Farm John需要你的帮助,计算FJ可以得到的最大效率,并且该方案中没有连续的超过K只奶牛。
输入格式
第一行:空格隔开的两个整数 N 和 K
第二到 N+1 行:第 i+1 行有一个整数 E_i
输出格式
第一行:一个值,表示 Farm John 可以得到的最大的效率值。
输入输出样例
输入 #1
5 2
1
2
3
4
5
输出 #1
12
【思路】
单调队列 线性DP
【说在前面的话】
很有意思的一道题
正着想那是选择最多的而且区间大小不定
所以很麻烦对不对
不想做这道题目了对不对?
【前缀思路】
正着不行那就反着来!
找可以选择的奶牛很麻烦
那就找出不选的奶牛
不选的奶牛一定是小的
而且是不会和别的成一个区间
因为有两个连续的不选择的奶牛不会让结果更优
只会让结果更差
因为多选一个比少选一个会更优的
具体证明就不多说了
自己感性理解一下下就好了
【最终思路】
然后从第一个点开始枚举
入队
如果这队首的到这个点的距离大于了k + 1那就弹出队首
为什么是k + 1呢
因为这是不选择的牛
最长的连续区间是k的长度
不选择的牛就会在这个区间的两边
这种情况下是距离k + 1
并且这种情况是最长情况
所以是大于k + 1
然后因为要选择最小的嘛
那就把队尾大于这个f[i]那也弹出
因为有了比队尾更小的值那队尾就没有可能会出现了
比我小还比我强!
【注意】
要先弹出队首超出范围的值
然后再处理出f[i]的值
最后在弹出队尾的
因为f[i]的值是取决于前面合法区间内的最小值
要先把区间内不合法的弹掉
不然不能保证f[i]合法
而且弹出的比较依据就是f[i]的大小
所以先在弹出队尾之前处理出f[i]的值
这样顺序就出来了吼
【完整代码】
#include<iostream>
#include<cstdio>
#include<queue>
#define int long long
using namespace std;
const int Max = 100005;
struct node
{
int t;
int v;
};
deque<node>q;
int a[Max];
int f[Max];
signed main()
{
int n,k;
cin >> n >> k;
int tot = 0;
for(register int i = 1;i <= n;++ i)
cin >> a[i],tot += a[i];
int M = 0x7f7f7f7f7f7f;
q.push_back((node){0,0});
for(register int i = 1;i <= n;++ i)
{
while(!q.empty() && i - q.front().t > k + 1)
q.pop_front();
f[i] = q.front().v + a[i];
while(!q.empty() && f[i] < q.back().v)
q.pop_back();
q.push_back((node){i,f[i]});
}
for(register int i = n;i >= n - k;i --)
M = min(M,f[i]);
cout << tot - M << endl;
return 0;
}