1. 程式人生 > >BZOJ 2442[Usaco2011 Open] 修剪草坪 (dp+單調佇列)

BZOJ 2442[Usaco2011 Open] 修剪草坪 (dp+單調佇列)

Description

在一年前贏得了小鎮的最佳草坪比賽後,FJ變得很懶,再也沒有修剪過草坪。現在,
新一輪的最佳草坪比賽又開始了,FJ希望能夠再次奪冠。然而,FJ的草坪非常髒亂,因此,FJ只能夠讓他的奶牛來完成這項工作。FJ有N
(1 <= N <= 100,000)只排成一排的奶牛,編號為1…N。每隻奶牛的效率是不同的,
奶牛i的效率為E_i(0 <= E_i <= 1,000,000,000)。

靠近的奶牛們很熟悉,因此,如果FJ安排超過K只連續的奶牛,那麼,這些奶牛就會罷工
去開派對:)。因此,現在FJ需要你的幫助,計算FJ可以得到的最大效率,並且該方案中
沒有連續的超過K只奶牛。

Input

* 第一行:空格隔開的兩個整數N和K* 第二到N+1行:第i+1行有一個整數E_i

Output

* 第一行:一個值,表示FJ可以得到的最大的效率值。

Sample Input

5 2
1
2
3
4
5輸入解釋:

FJ有5只奶牛,他們的效率為1,2,3,4,5。他們希望選取效率總和最大的奶牛,但是
他不能選取超過2只連續的奶牛

Sample Output


12FJ可以選擇出了第三隻以外的其他奶牛,總的效率為1+2+4+5=12。

題意就是讓你選若干段連續的長度不超過k的子段,讓總的和最大。

思路就是dp嘛: dp[i]表示選擇第i頭奶牛且i前面的選擇都是合法的,如果從dp[j]轉移過來,這樣的話j+1就不選,要保證值最大,所以j+2到i之間的都要選

轉移就是 dp[i] = dp[j] + sum[i] - sum[j+2-1]   ( i-k-1 <=j <= i-1)

直接轉移肯定n^2的,是過不了的,發現j的範圍是一個區間,所以可以用單調佇列來優化,儲存dp[j] - sum[j+1]的最大值即可。

#include<list>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long

using namespace std;



list<int> q;
LL sum[100010];
LL dp[100010];

int main(void)
{
    int n,k,i,j;
    while(scanf("%d%d",&n,&k)==2)
    {
        sum[0] = 0;
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&sum[i]);
            sum[i] += sum[i-1];
        }
        q.push_front(0);
        dp[0] = 0;
        for(i=1;i<=n;i++)
        {
            while(!q.empty() && dp[q.front()] - sum[q.front()+1] <= dp[i-1] - sum[i])
                q.pop_front();
            q.push_front(i-1);
            while(!q.empty() && i - (q.back()+2) + 1 > k)
                q.pop_back();
            if(i <= k)  //i <= k時的dp就是sum陣列的值
                dp[i] = sum[i];
            else
                dp[i] = dp[q.back()] + sum[i] - sum[q.back()+1];
        }
        printf("%lld\n",dp[n]);
    }

    return 0;
}