Beijing Guards---UVALive3177长城守卫--二分及其注意事项(mid的两种取值与二分下界)

题目链接https://vjudge.net/contest/305270#problem/P

在这里插入图片描述
在这里插入图片描述


题目大意:n个人围成一圈,其中第i个人想要xi个不同的礼物。相邻的两个人如果拥有同一种礼物那么双方都会不高兴。问一共需要多少种不同的礼物才能满足所有人的需求。

对于n为偶数的情况下答案是比较简单的,我们直接取相邻相加的最大值就行了:
在这里插入图片描述
偶数情况直接相互交错就行了:

a[n+1]=a[1];
for (int i=1; i<=n; i++) {
	ans=max(ans,a[i]+a[i+1]);
}

对于奇数的情况,我们可以二分答案,首先它绝对会比当做偶数的情况来得大,也就是说我们可以以ans为下界(至于为什么一定要ans为下界最后会有说明),至于上界我们随便设个1e8就行了。

接下来就是这么检查了,我们将第一个人的需求作为分界点,需求以内的为前方,需求以外的为后方:
在这里插入图片描述
接下来我们对于i为奇数时尽量往后取(即从x1这个分界点往后取),i为偶数时往前取。那么对于n=5,A={2,2,5,2,5}的情况假设我们二分的答案为8,第一个人取1,2,那么2就是分界点。接下来第二个人取3,4(先取1,2,但1,2被相邻的取了,只能往后再取两个),第三人取5,6,7,8,2,……

那么这一段的程序也就可以给出来了(我们的数组中保留的是对于前方和后方取了几个数):

L[1]=a[1],R[1]=0;
int xx=a[1],yy=x-a[1];
for (int i=2; i<=n; i++) {
	if (i%2) {//往后取
		R[i]=min(yy-R[i-1],a[i]);//后面取了多少个
		L[i]=a[i]-R[i];//后面不够的前面补
	} else {//往前取
		L[i]=min(xx-L[i-1],a[i]);
		R[i]=a[i]-L[i];
	}
}
if (L[n]==0) return 1;//最后一个不能再前方取数
return 0;

以下是AC代码:

#include <bits/stdc++.h>
using namespace std; 
const int mac=1e5+10;
const int inf=1e9+10;
int a[mac],L[mac],R[mac],n;
int ok(int x);
int main()
{
	while (scanf ("%d",&n)){
		if (!n) break;
		for (int i=1; i<=n; i++){
			scanf ("%d",&a[i]);
		}
		if (n==1){
			printf ("%d\n",a[1]);continue;
		}
		a[n+1]=a[1];
		int ans=0;
		for (int i=1; i<=n; i++){
			ans=max(ans,a[i]+a[i+1]);
		}
		if (n%2){
			int l=ans,r=inf,mid;
			for (int i=1; i<=32; i++){
				mid=l+(r-l)/2;
				if (ok(mid)){
					ans=mid;
					r=mid-1;
				}
				else l=mid+1;
			}
		}
		printf ("%d\n",ans);
	}
	return 0;
}
int ok(int x)
{
	L[1]=a[1],R[1]=0;
	int xx=a[1],yy=x-a[1];
	for (int i=2; i<=n; i++){
		if (i%2){
			R[i]=min(yy-R[i-1],a[i]);
			L[i]=a[i]-R[i];
		}
		else{
			L[i]=min(xx-L[i-1],a[i]);
			R[i]=a[i]-L[i];
		}
	}
	if (L[n]==0) return 1;
	return 0;
}

注意事项:1.对于二分的m=(l+r)/2和m=l+(r-l)/2
当l可能大于r的时候第一个会有问题,只能使用第二个,也就是说对于上题使用while 的时候两个都没问题,但使用for的时候只能使用第二个。

2.对于二分的下界:
如果下界太小的话可能导致一种情况,即二分的答案小于需求,那么就会使得L或R成为负数,但L[n]只能判断0,也就是说对于负数的情况他也会return 1。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值