题目描述
在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠。
然而,Farm John的草坪非常脏乱,因此,Farm John只能够让他的奶牛来完成这项工作。Farm John有N(1 <= N <= 100,000)只排成一排的奶牛,编号为1...N。每只奶牛的效率是不同的,奶牛i的效率为E_i(0 <= E_i <= 1,000,000,000)。
靠近的奶牛们很熟悉,因此,如果Farm John安排超过K只连续的奶牛,那么,这些奶牛就会罢工去开派对:)。因此,现在Farm John需要你的帮助,计算FJ可以得到的最大效率,并且该方案中没有连续的超过K只奶牛。
输入格式
第一行:空格隔开的两个整数 N 和 K
第二到 N+1 行:第 i+1 行有一个整数 E_i
输出格式
第一行:一个值,表示 Farm John 可以得到的最大的效率值。
输入输出样例
输入 #1
5 2 1 2 3 4 5
输出 #1
12
思路
动态规划+单调队列好题。
先考虑使用线性dp。令dp[i]表示前i头奶牛能得到的最大效率。在第i头奶牛,一定在区间[i-k,i]内有奶牛j得休息。在区间[i-k,j]内枚举休息的奶牛,则
dp[i]=max(dp[i],dp[i-1]+sum[i]-sum[j])
其中sum[i]表示前缀和,即奶牛区间[1,i]的效率和,sum[i]-sum[j]为奶牛区间[j,i]的效率和。答案为dp[n]。这个方程还是比较容易的吧。
于是我们写出代码,发现只有60分。2个WA,2个TLE。
#include <stdio.h>
#include <iostream>
using namespace std;
int n,k,a[100001],s,dp[100001],sum[100001];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
register int i,j;
cin>>n>>k;
for(i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];//前缀和
}
for(i=1;i<=n;i++)
{
for(j=i-k;j<=i;j++)
{
dp[i]=max(dp[i],dp[j-1]+sum[i]-sum[j]);
}
}
cout<<dp[n]<<endl;
return 0;
}
为什么会T呢?因为n达到了10^5。显然2层循环会TLE。
我们再仔细思考一下,发现如果想要dp[i]尽可能大,不就是想让dp[j-1]+sum[i]-sum[j]尽可能大吗?
那我们把方程变形,得:
dp[i]=max(dp[i],dp[j-1]-sum[j])+sum[i]
此时i-k<=j<=i。
即把sum[i]放到外面,这样保证了想让dp[i]尽可能大,就只和j有关系了,即max内的值只和j有关。现在只需要用单调队列维护dp[j-1]-sum[j]就好了。
按照这个思路,还是60。。WA4个点,发现这题要开long long。。不开long long见祖宗啊。
#include <stdio.h>
#include <iostream>
#include <deque>
#define ll long long int
using namespace std;
ll n,k,a[100001],s,dp[100001],sum[100001];
struct node
{
ll v,position;//v为权,position是下标
};
deque<node> dq;
inline void update(ll i)
{
ll x(dp[i-1]-sum[i]);//维护dp[j-1]-sum[j]
while(!dq.empty() && dq.back().v<=x) dq.pop_back();//如果队尾小于dp[j-1]-sum[j],全扔掉
dq.push_back({x,i});//新元素放进去
}
inline ll query(ll i)
{
while(!dq.empty() && dq.front().position<i-k) dq.pop_front();//检查是否在区间[i-k,i]内
return dq.front().v;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
register ll i,j;
cin>>n>>k;
for(i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];//前缀和
}
dq.push_back({0,0});//先压一个元素
for(i=1;i<=n;i++)
{
update(i);
dp[i]=query(i)+sum[i];//dp[i]=max(dp[i],dp[j-1]-sum[j])+sum[i]
}
cout<<dp[n]<<endl;
return 0;
}