题目链接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。