【BZOJ 2151】种树

【题目】

题目传送门种树

题目描述:

A 城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。园林部门得到指令后,初步规划出 n 个种树的位置,顺时针编号 1 到 n。并且每个位置都有一个美观度 Ai,如果在这里种树就可以得到这 Ai 的美观度。但由于 A 城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i 号位置和 i + 1 号位置叫相邻位置。值得注意的是 1 号和 n 号也算相邻位置!)。最终市政府给园林部门提供了 m 棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将 m 棵树苗全部种上,给出无解信息。

输入格式:

输入的第一行包含两个正整数 n、m 。
第二行n个整数 Ai。

输出格式:

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出“Error!”,不包含引号。

样例数据:

【样例1】

输入

7 3
1 2 3 4 5 6 7

输出

15

【样例2】

输入

7 4
1 2 3 4 5 6 7

输出

Error!

备注:

【数据规模】

对于全部数据:m ≤ n;-1000 ≤ Ai ≤ 1000

N的大小对于不同数据有所不同:

数据编号N的大小数据编号N的大小
13011200
235122007
340132008
445142009
550152010
655162011
760172012
86518199999
920019199999
1020020200000

 

【分析】

无解情况:如果 2 * m > n,那么就无解(因为树不能种在相邻的位置)

先考虑一下这道题的简化版,即如果树可以种在相邻位置,那我们直接从大到小排序选前 m 个就可以了

现在加了限制,假设第一次选的时候 A[ x ] 最大,按照之前的思路,我们试图选 A[ x ],然后同时删去 A[ x ],A[ x - 1 ],A[ x + 1 ],不过这样做有一个问题,就是虽然 A[ x - 1 ] 和 A[ x + 1 ] 都小于 A[ x ],但是有可能 A[ x - 1 ] + A[ x + 1 ] 就比 A[ x ] 大了,这种时候肯定选 A[ x - 1 ] 和 A[ x + 1 ] 比选 A[ x ] 更优

正解的做法是选到 A[ x ] 的时候,还是将 A[ x - 1 ] 和 A[ x + 1 ] 删掉,将 A[ x ] 的值改为 A[ x - 1 ] + A[ x + 1 ] - A[ x ],这相当于有一个“反悔”的操作,如果后来再次选到了 A[ x ],那就相当于选到了 A[ x - 1 ] 和 A[ x + 1 ]

由于有删点的操作,所以说到后面 x 两边的点不一定就是 x - 1 和 x + 1,我们要用两个数组记录一下它两边的点

 

【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
int a[N],l[N],r[N];
bool del[N];
priority_queue<pair<int,int> >q;
int main()
{
	int n,m,i,x,t,ans=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	if(2*m>n)
	{
		printf("Error!");
		return 0;
	}
	for(i=1;i<=n;++i)
	{
		l[i]=i-1;
		r[i]=i+1;
		q.push(make_pair(a[i],i));
	}
	l[1]=n;
	r[n]=1;
	memset(del,false,sizeof(del));
	for(i=1;i<=m;++i)
	{
		t=q.top().first;
		x=q.top().second;
		q.pop();
		while(del[x])
		{
			t=q.top().first;
			x=q.top().second;
			q.pop();
		}
		ans+=t;
		a[x]=a[l[x]]+a[r[x]]-a[x];
		del[l[x]]=true;
		del[r[x]]=true;
		l[x]=l[l[x]];
		r[x]=r[r[x]];
		r[l[x]]=x;
		l[r[x]]=x;
		q.push(make_pair(a[x],x));
	}
	printf("%d",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值