Problem2 修剪草坪(mowlawn.cpp/c/pas)
【题目描述】
在一年前赢得了小镇的最佳草坪比赛后,FJ变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,FJ希望能够再次夺冠。
然而,FJ的草坪非常脏乱,因此,FJ只能够让他的奶牛来完成这项工作。FJ有N (1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,奶牛i的效率为E_i(0 <= E_i <= 1,000,000,000)。靠近的奶牛们很熟悉,因此,如果FJ安排超过K(1<=K<=N)只连续的奶牛,那么,这些奶牛就会罢工去开派对:)。因此,现在FJ需要你的帮助,计算FJ可以得到的最大效率,并且该方案中没有连续的超过K只奶牛。
【输入格式】
* 第一行:空格隔开的两个整数N和K
* 第二到N+1行:第i+1行有一个整数E_i
【输出格式】
* 第一行:一个值,表示FJ可以得到的最大的效率值。
【样例输入】
5 2
1
2
3
4
5
输入解释:
FJ有5只奶牛,他们的效率为1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是
他不能选取超过2只连续的奶牛
【样例输出】
12
FJ可以选择出了第三只以外的其他奶牛,总的效率为1+2+4+5=12。
【题解】
设计状态:
f[i][j]表示在考虑前i只奶牛的情况下,从第i只奶牛开始选择了连续j头奶牛的最大效率
状态转移方程:
当j=0时,f[i][j]=max(f[i-1][0]...f[i-1][K])
当j>=1且j<=k时f[i][j]=f[i-1][j-1]+e[i]
时间复杂度O(NK) 爆炸
优化:
当j>=1时转移是O(1)的,这好像没什么好优化的
当j==0时,你要用到f[i-1][从0到K]的所有状态的最大值
我们立马就想到可以每次做一层就同时维护这一层的最大值,下一次转移直接O(1)
但是每算一层新的数,还是要O(K)重新计算最大值,怎么优化呢?
我们只需要最后一层的最大值作为答案
我们是否需要计算所有的中间状态?
f[1][0] f[1][2] f[1][3] f[1][4]
f[2][0] f[2][2] f[2][3] f[2][4]
f[3][0] f[3][2] f[3][3] f[3][4]
每一层的第一个元素也就是f[i][0],都是前面一层的最大值,其他元素是其左上方的元素加上e[i]得到的,那么这几个数的相对大小不改变。如果上一层的最大值在最后一个数取到,那么这一层的最大值就是max(f[i][0],上一层的次大值+e[i]);如果上一层的最大值不在最后一个数取到,那么这一层的最大值是上一层的最大值+e[i]
仿佛算法的雏形已经显现了,但仔细思考有很大的问题,如果有了最大值、次大值,那么转移就是O(1)的。考虑这样一种情况:上一层的最大值在最后一个数取到,那么你就要用上一层的次大值转移,这很容易,但是这样一来你还要计算这一层的次大值,而次大值的计算没有捷径还是只能O(K)扫描(当然你可以用数据结构优化到O(logK)但这不是今天讨论的重点)。这该怎么搞呢?
我们一直在讨论最大值、次大值,以及删除最大值之后该怎样找到新的次大值的问题,这很容易让我们想到单调数据结构,这道题用到单调队列。
维护一个严格单调下降的单调队列(权值,第二下标),假如第i-1层已经计算完了。那么f[i][0]直接取队首就好了,设一个d表示增量,直接给d加上e[i]就表示整个队列里的元素加上了e[i],用队列里的元素的时候就这样q[l]+d,入队的时候要入(x-d);下标的处理同理,就用dk表示增量,每次+1。为了准备下一次转移,我们要把f[i][0]放进去,从队尾开始进入,把所有小于等于它的元素全部剔除,然后将它放在队尾。再从队首开始把所有下标大于K的元素踢掉。循环往复。。。
其实说的不是很清楚。。。(无所谓咯)
【代码】
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 100050
using namespace std;
struct queue
{ll w, pos;}q[maxn];
ll l, r, N, K;
void work()
{
ll i, j, e, dw=0, dk=0;
l=1,r=1;
scanf("%I64d%I64d",&N,&K);
scanf("%I64d",&e);
q[r++]=(queue){e,1};
q[r++]=(queue){0,0};
for(i=2;i<=N;i++)
{
scanf("%I64d",&e);
dw+=e;
dk++;
for(;q[r-1].w+dw<=q[l].w+dw-e;r--);
q[r++]=(queue){q[l].w-e,-dk};
for(;q[l].pos+dk>K;l++);
}
printf("%I64d\n",q[l].w+dw);
}
int main()
{
freopen("mowlawn.in","r",stdin);
freopen("mowlawn.out","w",stdout);
work();
return 0;
}