题意: n个人围成一圈,第i个人想要ri个不同的礼物,相邻的两个人礼物类型不能重复。每种礼物不限量,求最少需要多少种礼物才行(1<=n<=100000)
题解: 首先礼物种类数目res肯定大于等于相邻种类数之和的最大值x
假如n为偶数,则任何2个相邻的人需求分别是a和b,a+b<=x,只需要让编号为奇数的取x种礼物种的前a种,编号为偶数的取后b种即可满足题意,所以n偶数时只要res取x就可以了。
n为奇数的话就得二分答案了,假设答案为res,第一个人取前r[1]种礼物,然后把礼物分成2堆,前r[1]种一堆,剩下再为一堆,然后往后遍历的时候,让编号奇数的人在可以选的礼物中选择编号尽量靠前的,偶数的人选靠后的,这样就是最优的策略。
注意考虑n==1的特殊情况
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100010
#define INF 100000000
using namespace std;
int n;
int r[MAXN];
int lcnt = 0, rcnt = 0;
bool isok(int number) {
lcnt = r[1];
rcnt = number - lcnt;
// 第一个人取1...r[1]糖果
// 然后把糖果分成2部分
// 1...r[1]为一部分
// 剩下的为另一部分
// 每次判断每部分剩下多少,最后判断第一个人
// 和最后一个人有没有冲突即可
int lleft = 0;
int rleft = rcnt;
for(int i = 2;i <= n;i++) {
int need = r[i]; // 需要的糖果数
// 剩下的糖果不够,则返回false
if(need > lleft + rleft) return false;
if(i%2 == 0) {
// 偶数编号的人尽量取前面的糖果
if(lleft >= need) {
lleft = lcnt - need;
rleft = rcnt;
} else {
rleft = rcnt - (need - lleft);
lleft = lcnt - lleft;
}
} else {
// 奇数编号的人尽量取后面的糖果
if(rleft >= need) {
rleft = rcnt - need;
lleft = lcnt;
} else {
lleft = lcnt - (need - rleft);
rleft = rcnt - rleft;
}
}
}
return lleft == lcnt;
}
int main() {
//freopen("out.txt","w",stdout);
while(scanf("%d",&n) != EOF && n != 0) {
memset(r,0,sizeof(r));
int Max = 0; // 糖果种类数的上限
int Min = -1; // 糖果种类数的下限
if(n == 1) {
scanf("%d",&r[1]);
printf("%d\n",r[1]);
continue;
}
for(int i = 1;i <= n;i++) {
scanf("%d",&r[i]);
if(i>1)
Min = max(r[i]+r[i-1],Min);
if(i == n)
Min = max(Min,r[n]+r[1]);
Max += r[i];
}
if(n % 2 == 0) {
printf("%d\n",Min);
} else {
// 二分答案
int L = Min;
int R = Max;
while(L < R) {
int M = (L+R) / 2;
if(isok(M)) {
R = M;
} else {
L = M + 1;
}
}
printf("%d\n",L);
}
}
return 0;
}