题目描述
在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠。
然而,Farm John的草坪非常脏乱,因此,Farm John只能够让他的奶牛来完成这项工作。Farm John有 N ( 1 < = N < = 100 , 000 ) N(1 <= N <= 100,000) N(1<=N<=100,000)只排成一排的奶牛,编号为 1... N 1...N 1...N。每只奶牛的效率是不同的,奶牛i的效率为 E i ( 0 < = E i < = 1 , 000 , 000 , 000 ) E_i(0 <= E_i <= 1,000,000,000) Ei(0<=Ei<=1,000,000,000)。
靠近的奶牛们很熟悉,因此,如果Farm John安排超过K只连续的奶牛,那么,这些奶牛就会罢工去开派对:)。因此,现在Farm John需要你的帮助,计算FJ可以得到的最大效率,并且该方案中没有连续的超过K只奶牛。
输入格式:
第一行:空格隔开的两个整数$ N$ 和$ K$
第二到 $N+1 $行:第 i + 1 i+1 i+1 行有一个整数 E i E_i Ei
输出格式:
第一行:一个值,表示 Farm John 可以得到的最大的效率值。
输入样例#1:
5 2
1
2
3
4
5
输出样例#1:
12
这个题让我学会了怎么用单调队列来优化dp
-
首先我们来看题目,不能有连续的 k + 1 k + 1 k+1头牛连续,被安排的牛非常多。所以我们可以让没有被安排的牛作为状态。
-
设 d p [ i ] dp[i] dp[i] 为第i头牛不被安排的最大效率。
-
则我们可以得到转移方程
d p [ i ] = d p [ j ] + Σ ( d p [ j + 1 ] d p [ i − 1 ] ) ( i > j , i − j < = k + 1 ) dp[i] = dp[j] + Σ(dp[j + 1] ~ dp[i - 1]) (i > j , i - j <= k + 1) dp[i]=dp[j]+Σ(dp[j+1] dp[i−1])(i>j,i−j<=k+1)
(i 和 j 不被安排则他们中间的牛会被安排)
这样的话我们可以轻松写出代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 23333;
inline int read()
{
int x = 0 , f = 1;
char ch = getchar();
while(ch < '0'|| ch >'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x = x * 10 + ch - 48;ch = getchar();}
return x * f;
}
int n , k;
int a[maxn] , dp[maxn] , p[maxn];
int main()
{
n = read(); k = read();
for(int i = 1 ; i <= n ; i ++) a[i] = read();
for(int i = 1 ; i <= n ; i ++) p[i] = p[i - 1] + a[i];
for(int i = 1 ; i <= n + 1; i ++)
for(int j = 0 ; j < i ; j ++)
if(i - j <= k + 1)
dp[i] = max(dp[i] , dp[j] + p[i - 1] - p[j]);
cout<<dp[n + 1];
return 0;
}
Σ ( d p [ j + 1 ] d p [ i − 1 ] ) Σ(dp[j + 1] ~ dp[i - 1]) Σ(dp[j+1] dp[i−1])我们可以用前缀和维护
但是这样的复杂度是接近 O ( n 2 ) O(n^2) O(n2) 的 而n是十万肯定过不了
我们现在观察式子 d p [ i ] = d p [ j ] + Σ ( d p [ j + 1 ] d p [ i − 1 ] ) dp[i] = dp[j] + Σ(dp[j + 1] ~ dp[i - 1]) dp[i]=dp[j]+Σ(dp[j+1] dp[i−1])
发现dp[i]只会被自己之前的最大的j更新,所以我们维护一个单调递减的单调队列
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 123333;
inline int read()
{
int x = 0 , f = 1;
char ch = getchar();
while(ch < '0'|| ch >'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x = x * 10 + ch - 48;ch = getchar();}
return x * f;
}
struct node
{
long long num , p;
}e[maxn];
//e[].p 维护一个单调递减的队列 dp[i] - p[i]
long long n , k , head = 1 , tail = 0;
long long a[maxn] , dp[maxn] , p[maxn];
int main()
{
n = read(); k = read();
for(int i = 1 ; i <= n ; i ++) a[i] = read();
for(int i = 1 ; i <= n ; i ++) p[i] = p[i - 1] + a[i];
for(int i = 0 ; i <= n + 1; i ++)
{
while(i - e[head].num >= k + 2 && head <= tail ) head++;
dp[i] = e[head].p + p[i - 1];
while(dp[i] - p[i] >= e[tail].p && head <= tail) tail --;
e[++tail].p = dp[i] - p[i];
e[tail].num = i;
}
cout<<dp[n + 1];
return 0;
}
这样我们就能水过这个题了