BZOJ 2442[Usaco2011 Open] 修剪草坪 (dp+單調佇列)
阿新 • • 發佈:2019-01-29
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; }