【DP】【cofun1041】修建草坪

来源:互联网 发布:淘宝店铺链接在哪复制 编辑:程序博客网 时间:2024/04/27 22:09

【cofun1041】修建草坪

Description
在去年赢得了小镇的最佳草坪比赛后,约翰变得懒惰了,再也没有修剪过草坪了。现在,新一轮的比赛又开始了,约翰希望能够再次夺冠。然而,约翰的草坪非常脏乱,因此,约翰需要让他的奶牛来完成这项工作。约翰有N头奶牛,平时排成一条直线,编号为1到N。每只奶牛的能力是不同的,第i头奶牛的能力为Ei。靠在一起的奶牛很熟悉,所以如果安排编号连续的K + 1头奶牛在一起工作,她们就会密谋罢工。因此,约翰需要你的帮助。如何挑选奶牛,才能使她们的工作能力之和最高,而且不会罢工呢?

Input Format
第一行:两个用空格隔开的整数:N和K,1 ≤ N ≤ 105,1 ≤ K ≤ N
第二行到N + 1行:第i + 1行有一个整数,表示第i头牛的能力Ei,1 ≤ Ei ≤ 109
Output Format
第一行:单个整数,表示最大的工作能力之和

Sample Input
5 2
1
2
3
4
5
Sample Output
12

Hint
除了第三头以外的所有奶牛都选,总能力 为1 + 2 + 4 + 5 = 12

Source
USACO 2011 open


分析:
满足无后效性,DP。
1. 求出能力的前缀和。
2. n <= 8000时,直接DP,转移方程:

for(i = 1; i <= k; i ++)    f[i] = s[i];for(i = k + 1; i <= n; i ++){    f[i] = f[i - 1];    for(j = i - 2; j >= i - k - 1; j --)    f[i] = max(f[i], f[j] + s[i] - s[j + 1]); }

f[i]:到第i只奶牛为止满足要求的最高工作能力。
3. n <= 105时,考虑降维。可以发现,每次更新f[i]就是要找到连续一段与i一起工作,使得能力最大。参考上面的方程,可以利用优化队列存下f[j]-s[j+1],转移方程:

for(i = 1; i <= n; i ++){    for(; head <= tail && q[head][0] <= i - k - 1; head ++);    f[i] = max(f[i - 1], q[head][1] + s[i]);    for(; head <= tail && q[tail][1] < f[i - 1] - s[i]; tail --);    q[++ tail][0] = i, q[tail][1] = f[i - 1] - s[i]; }

  • 代码:
#include <bits/stdc++.h> using namespace std; int n, k, i, j, e, head, tail; long long f[100005], s[100005], q[100005][2]; int main() {    scanf("%d%d", &n, &k);    for(i = 1; i <= n; i ++)         scanf("%d", &e), s[i] = s[i - 1] + e;    /*for(i = 1; i <= k; i ++)        f[i] = s[i];    for(i = k + 1; i <= n; i ++)    {        f[i] = f[i - 1];        for(j = i - 2; j >= i - k - 1; j --)        f[i] = max(f[i], f[j] + s[i] - s[j + 1]);    }*/    for(i = 1; i <= n; i ++)    {        for(; head <= tail && q[head][0] <= i - k - 1; head ++);        f[i] = max(f[i - 1], q[head][1] + s[i]);        for(; head <= tail && q[tail][1] < f[i - 1] - s[i]; tail --);        q[++ tail][0] = i, q[tail][1] = f[i - 1] - s[i];    }    printf("%lld", f[n]);    return 0; }
原创粉丝点击