1599:【 例 3】修剪草坪

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 行有一个整数 E_{i}

【输出】

一行一个值,表示 FJ 可以得到的最大的效率值。

【输入样例】

5 2
1
2
3
4
5

【输出样例】

12

【提示】

样例说明:

FJ 有 55 只奶牛,他们的效率为 1,2,3,4,5。他们希望选取效率总和最大的奶牛,但是他不能选取超过 22 只连续的奶牛。FJ 选择出了第三只以外的其他奶牛,总的效率为 1+2+4+5=12。

数据范围与提示:

对于全部数据,1\leq N \leq 10^{5},0\leq E _{i} \leq 10^{9} 。


一.暴搜

传递三个变量

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]);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值