uva 1335 Beijing Guards

点击打开链接uva1335

思路:二分+贪心
分析:
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;
}




你可能感兴趣的:(uva 1335 Beijing Guards)