1599:【 例 3】修剪草坪
时间限制: 1000 ms 内存限制: 524288 KB
提交数: 1472 通过数: 701
【题目描述】
原题来自:USACO 2011 Open Gold
在一年前赢得了小镇的最佳草坪比赛后,FJ 变得很懒,再也没有修剪过草坪。现在,新一轮的最佳草坪比赛又开始了,FJ 希望能够再次夺冠。
然而,FJ 的草坪非常脏乱,因此,FJ 只能够让他的奶牛来完成这项工作。FJ 有 N 只排成一排的奶牛,编号为 1 到 N。每只奶牛的效率是不同的,奶牛 ii 的效率为 EiEi 。
靠近的奶牛们很熟悉,如果 FJ 安排超过 KK 只连续的奶牛,那么这些奶牛就会罢工去开派对。因此,现在 FJ 需要你的帮助,计算 FJ 可以得到的最大效率,并且该方案中没有连续的超过 KK 只奶牛。
【输入】
第一行:空格隔开的两个整数 N 和 K;
第二到 N+1 行:第 i+1 行有一个整数 。
【输出】
一行一个值,表示 FJ 可以得到的最大的效率值。
【输入样例】
5 2
1
2
3
4
5
【输出样例】
12
【提示】
样例说明:
FJ 有 55 只奶牛,他们的效率为 1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是他不能选取超过 22 只连续的奶牛。FJ 选择出了第三只以外的其他奶牛,总的效率为 1+2+4+5=12。
数据范围与提示:
对于全部数据, 。
一.暴搜
传递三个变量
id代表当前到第几头牛
ans代表当前的总和
v代表连续的几头牛
#include<iostream>
using namespace std;
int a[10000000],b[10000000];
int n,m;
int maxx;
void Dfs(int id,int ans,int v)
{
if(v>m) //超过m
return;
if(id==n+1)
{
maxx=max(maxx,ans);
return;
}
Dfs(id+1,ans+a[id],v+1); //选
Dfs(id+1,ans,0); //不选
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
Dfs(1,0,0);
cout<<maxx;
}
二.动态规划
状态:我们可以定义d[i][1]为:在选第i个物品的情况下,当前的最大值
d[i][0]为:在不选第i个物品的情况下,当前的最大值
d[1][1]=a[1] d[1][0]=0;
d[i][0]:既然第i个牛没选,那么他就等于他的前一个状态的最大值,即
d[i][1]:既然选了,又因为连续的牛不能超过m,所以就至少需要在(i-m)处断开,即不选第(i-m)头牛 那么就等于d[j][0]+(a[j+1]+a[j+2].......+a[i])的最大值
#include<iostream>
using namespace std;
int a[10000000],d[10000000][3];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
d[1][0]=0;
d[1][1]=a[1];
for(int i=2;i<=n;i++)
{
d[i][0]=max(d[i-1][0],d[i-1][1]);
for(int j=i-min(m,i);j<i;j++) //不能小于0,也不能等于i
{
int dd=0;
for(int k=j+1;k<=i;k++) //第a[j]个没选,则从j+1开始
dd=dd+a[k];
d[i][1]=max(d[i][1],d[j][0]+dd);
}
}
cout<<max(d[n][0],d[n][1]);
}
当然最内层循环也可以用前缀和来优化,不过对分数没有影响
#include<iostream>
using namespace std;
int a[10000000],d[10000000][3],s[10000000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
d[1][0]=0;
d[1][1]=a[1];
for(int i=2;i<=n;i++)
{
d[i][0]=max(d[i-1][0],d[i-1][1]);
for(int j=i-min(m,i);j<i;j++)
d[i][1]=max(d[i][1],d[j][0]+s[i]-s[j]);
}
cout<<max(d[n][0],d[n][1]);
}
这也是对接下来做的基础。
三.单调队列优化
有了动态规划做法,再转成单调队列,就容易一些了。
模版大家应该都会吧
#include<iostream>
using namespace std;
int a[10000000],d[10000000][3],s[10000000],q[10000000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
d[1][0]=0;
d[1][1]=a[1];
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(hh<=tt&&i-m>q[hh])
hh++;
d[i][0]=max(d[i-1][0],d[i-1][1]);
while(hh<=tt&& )
tt--;
q[++tt]=i;
}
cout<<max(d[n][0],d[n][1]);
}
由之前的动态规划,我们不难想出,d[i][1]都是由前面区间的d[j][0]在加上后面 从j+1到i 的最大值 观察前面这个式子
d[j][0]+s[i]-s[j]
我们可以得出每次 s[i] 中的 i 都是当前数的下标,很符合单调队列的循环变量i
所以,我们可以把 d[j][0]-s[j] 看作一个整体
于是,我们便可以转化为
在区间 (i-m , i) 中,这个整体的最大值。
于是
#include<iostream>
using namespace std;
int a[10000000],d[10000000][3],s[10000000],q[10000000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
d[1][0]=0;
d[1][1]=a[1];
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(hh<=tt&&i-m>q[hh])
hh++;
d[i][0]=max(d[i-1][0],d[i-1][1]);
d[i][1]=d[q[hh]][0]-s[q[hh]]+s[i];
while(hh<=tt&&d[q[tt]][0]-s[q[tt]]<=d[i][0]-s[i])
tt--;
q[++tt]=i;
}
cout<<max(d[n][0],d[n][1]);
}
由于在 q[++tt]=i 的作用下,第一次循环过后q[hh]便不为0,所以s[q[hh]]也不为0
当i还没有到m时,他便会多减去一个s[q[hh]]。
于是,完整代码如下
#include<iostream>
using namespace std;
int a[10000000],d[10000000][3],s[10000000],q[10000000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
d[1][0]=0;
d[1][1]=a[1];
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(hh<=tt&&i-m>q[hh])
hh++;
d[i][0]=max(d[i-1][0],d[i-1][1]);
if(i<=m)
d[i][1]=s[i];
else
d[i][1]=d[q[hh]][0]-s[q[hh]]+s[i];
while(hh<=tt&&d[q[tt]][0]-s[q[tt]]<=d[i][0]-s[i])
tt--;
q[++tt]=i;
}
cout<<max(d[n][0],d[n][1]);
}
你会发现,诶?怎么还有四个点答案错误?
十年OI一场空,不开long long见祖宗
#include<iostream>
using namespace std;
long long a[10000000],d[10000000][3],s[10000000],q[10000000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
d[1][0]=0;
d[1][1]=a[1];
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{
if(hh<=tt&&i-m>q[hh])
hh++;
d[i][0]=max(d[i-1][0],d[i-1][1]);
if(i<=m)
d[i][1]=s[i];
else
d[i][1]=d[q[hh]][0]-s[q[hh]]+s[i];
while(hh<=tt&&d[q[tt]][0]-s[q[tt]]<=d[i][0]-s[i])
tt--;
q[++tt]=i;
}
cout<<max(d[n][0],d[n][1]);
}