2442: [Usaco2011 Open]修剪草坪
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1159 Solved: 593
[Submit][Status][Discuss]
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
1
2
3
4
5
输入解释:
FJ有5只奶牛,他们的效率为1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是
他不能选取超过2只连续的奶牛
Sample Output
FJ可以选择出了第三只以外的其他奶牛,总的效率为1+2+4+5=12。
HINT
Source
这道题是个dp不难想
首先预处理出来前缀和sum数组
我们列出状态转移方程
用f[i]来表示在前i头奶牛中要第i头奶牛能获得最大的效率是多少
枚举j,强制第j头奶牛不选,那么f[i]=max(f[i],f[j-1]-sum[j]+sum[i]);
j的范围是i-k+1到i-1,复杂度最坏是n^2
把原方程分解我们发现只和f[j-1]-sum[j]有关
我们可以像之前一样用一个线段树之类的去维护
也可以用单调队列去维护f[j-1]-sum[j]
线段树有可能会被卡QAQ
还是单调队列大法好啊 O(n)
细节上的处理还是和之前的题目一样
具体参考代码:
#include <bits/stdc++.h> #define ll long long using namespace std; inline ll read(){ ll x=0;ll f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } const ll MAXN=1e6+10; namespace zhangenming{ ll f[MAXN],n,k,a[MAXN],sum[MAXN],q[MAXN],head=1,tail=0; void init(){ n=read();k=read(); memset(sum,0,sizeof(sum)); for(ll i=1;i<=n;i++){ a[i]=read(); sum[i]=sum[i-1]+a[i]; } } void solve(){ memset(f,0,sizeof(f)); for(ll i=1;i<=k;i++){ f[i]=sum[i]; ll tn=f[i-1]-sum[i]; while(head<=tail&&tn>f[q[tail]-1]-sum[q[tail]]) tail--; q[++tail]=i; //cout<<i<<' '<<f[i]<<' '<<tn<<endl; } for(ll i=k+1;i<=n;i++){ while(i-q[head]>k) head++; //cout<<i<<' '<<q[head]<<' '<<f[q[head]-1]-sum[q[head]]<<' '; f[i]=f[q[head]-1]-sum[q[head]]+sum[i]; ll tn=f[i-1]-sum[i]; //cout<<tn<<' '; while(tail>=head&&tn>f[q[tail]-1]-sum[q[tail]]) tail--; q[++tail]=i; //cout<<f[i]<<endl; } ll maxn=-1000000; for(ll i=1;i<=n;i++){ maxn=max(maxn,f[i]); } cout<<maxn<<endl; //cout<<f[n]<<endl; } } int main(){ using namespace zhangenming; init(); solve(); return 0; }#include <bits/stdc++.h> #define ll long long using namespace std; inline ll read(){ ll x=0;ll f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} return x*f; } const ll MAXN=1e6+10; namespace zhangenming{ ll f[MAXN],n,k,a[MAXN],sum[MAXN],q[MAXN],head=1,tail=0; void init(){ n=read();k=read(); memset(sum,0,sizeof(sum)); for(ll i=1;i<=n;i++){ a[i]=read(); sum[i]=sum[i-1]+a[i]; } } void solve(){ memset(f,0,sizeof(f)); for(ll i=1;i<=k;i++){ f[i]=sum[i]; ll tn=f[i-1]-sum[i]; while(head<=tail&&tn>f[q[tail]-1]-sum[q[tail]]) tail--; q[++tail]=i; //cout<<i<<' '<<f[i]<<' '<<tn<<endl; } for(ll i=k+1;i<=n;i++){ while(i-q[head]>k) head++; //cout<<i<<' '<<q[head]<<' '<<f[q[head]-1]-sum[q[head]]<<' '; f[i]=f[q[head]-1]-sum[q[head]]+sum[i]; ll tn=f[i-1]-sum[i]; //cout<<tn<<' '; while(tail>=head&&tn>f[q[tail]-1]-sum[q[tail]]) tail--; q[++tail]=i; //cout<<f[i]<<endl; } ll maxn=-1000000; for(ll i=1;i<=n;i++){ maxn=max(maxn,f[i]); } cout<<maxn<<endl; //cout<<f[n]<<endl; } } int main(){ using namespace zhangenming; init(); solve(); return 0; }
有一个失误的地方
最后其实可以不用求maxn,因为随着i的增加,f[i]会不严格单调递增,直接输出f[n]即可
注意过程中的变量要开long long
网上还有一些别的思路
就是把能获得最高多少效率转化为最少失去多少效率
接下来的步骤也是一样的只不过用单调队列或线段树去维护的值不一样了
代码这里就不给出了