洛谷P2627 修剪草坪(单调队列优化)

题目描述

在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠。

然而,Farm John的草坪非常脏乱,因此,Farm John只能够让他的奶牛来完成这项工作。Farm John有 N ( 1 &lt; = N &lt; = 100 , 000 ) N(1 &lt;= N &lt;= 100,000) N(1<=N<=100,000)只排成一排的奶牛,编号为 1... N 1...N 1...N。每只奶牛的效率是不同的,奶牛i的效率为 E i ( 0 &lt; = E i &lt; = 1 , 000 , 000 , 000 ) E_i(0 &lt;= E_i &lt;= 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 &gt; j , i − j &lt; = k + 1 ) dp[i] = dp[j] + Σ(dp[j + 1] ~ dp[i - 1]) (i &gt; j , i - j &lt;= k + 1) dp[i]=dp[j]+Σ(dp[j+1] dp[i1])(i>j,ij<=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[i1])我们可以用前缀和维护

但是这样的复杂度是接近 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[i1])

发现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;
}

这样我们就能水过这个题了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值