思路:二分+贪心
分析:
1 当n为偶数的时候,我们可以知道肯定会有两两相等的情况,那么这个时候ans = max(r[i]+r[i+1]),可以自己画个图验证;
2 当n为奇数的时候就不满足了,那么我们可以利用二分答案然后判断从而求出最小值。假设有p种礼物,那么设第一个人的礼物为1~r1,那么不难发现最有的分配的策略一定是这样的“编号为偶数的人尽量往前取,编号为奇数的人尽量往后取”,这样我们只要判断时候第n个人和第一个人有冲突即可。
3 由于题目并没有要我们输出实际的方案,那么我们只需要记录每个人在[1,r1]和[r1+1,n]这个范围内取了几个,最后判断第n个人是否有取[1,r1]范围内的数即可。
4 注意这边对于n = 1的情况需要进行特判
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 100010; int r[maxn]; bool ok(int num , int n){ int left[maxn] , right[maxn]; memset(left , 0 , sizeof(left)); memset(right , 0 , sizeof(right)); left[1] = r[1]; int x = r[1] , y = num-r[1]; for(int i = 2 ; i <= n ; i++){ if(i%2){//如果编号为奇数,尽量往后面取 right[i] = min(y-right[i-1] , r[i]); left[i] = r[i]-right[i]; } else{//如果编号为奇数,尽量往前面取 left[i] = min(x-left[i-1] , r[i]); right[i] = r[i]-left[i]; } } return left[n] == 0; } int solve(int n){ int left , right , mid; //求二分的边界 left = right = 0; for(int i = 1 ; i <= n ; i++){ left = max(left , r[i]+r[i+1]); right = max(right , r[i]*3); } while(left <= right){ mid = (left+right)>>1; if(ok(mid , n)) right = mid-1; else left = mid+1; } return left; } int main(){ int ans , n; while(scanf("%d" , &n) && n){ for(int i = 1 ; i <= n ; i++) scanf("%d" , &r[i]); if(n == 1){//特判 printf("%d\n" , r[1]); continue; } r[n+1] = r[1]; ans = 0; for(int i = 1 ; i <= n ; i++) ans = max(ans , r[i]+r[i+1]); if(n%2 == 0) printf("%d\n" , ans); else printf("%d\n" , solve(n)); } return 0; }