题目链接:https://vjudge.net/problem/UVA-1335
一道有思维的二分答案题目。
首先,如果$n$为偶数,那么$p=max(r_i+r_{i+1})$,不难看出,这个值便是答案的下限。
对于$n$为奇数的情况,我们就要二分答案:
首先以$r_1$为界,$left[i]$记录第$i$个人在$1$~$r_1$中取了多少个礼物,$right[i]$记录第$i$个人在$r_1+1$~$n$中取了多少个礼物。
假设有$p$种礼物,那么设第一个人的礼物是$1$~$r_1$,那么最优的策略便是:编号为偶数的人尽量往前取,编号为奇数的人尽量往后取。
其实很好理解:因为$1$本身即为奇数,$1$已经取了$1$~$r_1$,如果要使$n$($n$为奇数)的礼物最大限度不与$1$的不相同,那么就要尽可能多的取$right[]$中的礼物。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 const int maxn=100010; 6 int n,left[maxn],right[maxn],r[maxn]; 7 8 bool check(int p){ 9 int x=r[1],y=p-r[1]; 10 left[1]=x; right[1]=0; 11 for(int i=2;i<=n;i++){ 12 if(i&1){ 13 right[i]=min(r[i],y-right[i-1]); 14 left[i]=r[i]-right[i]; 15 } 16 else{ 17 left[i]=min(r[i],x-left[i-1]); 18 right[i]=r[i]-left[i]; 19 } 20 } 21 return left[n]==0; 22 } 23 24 int main(){ 25 while(scanf("%d",&n)==1&&n){ 26 for(int i=1;i<=n;i++) scanf("%d",&r[i]); 27 if(n==1){ 28 printf("%d ",r[1]); 29 continue; 30 } 31 r[n+1]=r[1]; 32 int L=0,R=0; 33 for(int i=1;i<=n;i++) L=max(L,r[i]+r[i+1]); 34 if(n&1){ 35 for(int i=1;i<=n;i++) R=max(R,r[i]*3); 36 while(L<R){ 37 int M=L+(R-L)/2; 38 if(check(M)) R=M; else L=M+1; 39 } 40 } 41 printf("%d ",L); 42 } 43 return 0; 44 }