有n个人围成一圈,每个人想要Ai种糖果。要求相邻的两个人不能有相同类型的糖果,问最小需要的糖果数量
分析:
n = 1,简单
n=偶数,简单,为相邻两个人糖果数量和的最小值,即max{Ai + A((i%n)+1)}
n=奇数
采取构造法:首先二分一个糖果数量M,然后以O(n)的时间复杂度内判断其是否成立
M的最小可能值为max{Ai + A((i%n)+1)},最大可能值为sum{Ai}
第1个人:取1 - Ai种
第2个人:取Ai + 1 - Ai + A(i+1)种
我们知道:相邻的两个人的分配方案,M一定可以解决
从此往后,
偶数标号的人取与前一个人不冲突的尽可能小的;
先假设我们想要取 1 - Ax,即L = 1,R = Ax;那么需要判断:L与R[x-1]的关系
奇数标号的人取与前一个人不冲突的尽可能大的;
先假设我们想要取 M - (M - Ax + 1),即L = M - Ax + 1,R = M,那么需要判断,L与R[x-1]的关系
于是:第n个人取得是尽可能大的,如果与第1个人不冲突,那么说明答案M可行
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 10;
int n;
int L[maxn], R[maxn], a[maxn];
bool check(int x){
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
L[1]=1;
R[1]=a[1];
for(int i=2;i<=n;i++)
if (i%2==0){
if (a[i]>=L[i-1]){
L[i]=R[i-1]+1;
R[i]=L[i]+(a[i]-(L[i-1]-1))-1;
}
else{
L[i]=1;
R[i]=a[i];
}
}
else{
if (x-a[i]+1<=R[i-1]){
R[i]=L[i-1]-1;
L[i]=R[i]-(a[i]-(x-R[i-1]))+1;
}
else{
R[i]=x;
L[i]=x-a[i]+1;
}
}
if (L[n]<=a[1]) return false;
return true;
}
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d",&n)==1 && n){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int M = 0, S = 0;
for(int i=1;i<=n;i++){
M=max(M,a[(i%n)+1]+a[i]);
S+=a[i];
}
if (n%2==0) printf("%d\n",M);
else{
int Min = M, Max = S, ans = S;
while(Min <= Max){
M = (Min + Max) >> 1;
if (check(M)){
Max = M - 1;
ans = M;
}
else{
Min = M + 1;
}
}
printf("%d\n",ans);
}
}
return 0;
}