UVALive 3177打卡

算法竞赛入门经典训练指南打卡

题目链接:UVALive 3177

思路

首先,毋庸置疑,肯定相邻的两个人是要分开拿礼物的,即不能拿到同一种,那么
1.对于偶数个人来说,只要第一个人和最后一个人完全可以按照奇偶错开
比如第一个人拿1 2 ,第二个人拿 3 4 ,第三个人又可以拿 1 , 2
	所以我们只需要找到相邻的两个人所要礼物加起来最多的礼物数即可
	定义i遍历数组       ans = max(r[i] + r[i + 1] , ans)
2.对于奇数个人来说,如果只是奇偶错开,则第一个人和最后一个人会重复
所以我们需要使用二分查找来找出合适的结果
那么对于奇数来说,可以让相邻的两个人错开拿,通过枚举来查看
	第一个人拿前面几个礼物
	奇数位的人尽量拿后面几个礼物(尽量指不与前面的人重复的情况下)
	偶数位的人尽量拿前面几个礼物
	则最后一个人,会尽量拿到后面几个礼物和第一个人错开

具体细节看代码注释

代码如下:

#include <iostream>
#define ton(i , n) for(int i = 1 ; i <= n ; ++ i)

using namespace std ;

const int max_n = 100010 ;
int n , r[max_n] , Left[max_n] , Right[max_n] ;

bool check(int mid){
    int x = r[1] , y = mid - r[1] ;     //第一个人拿前r[1]个 , 剩余的礼物数为mid - r[1]个
    Left[1] = x ;   //前x个礼物为左边,则第一个人从左边拿了x个礼物
    Right[1] = 0 ;  //第一个人没有拿右边的礼物
    for(int i = 2 ; i <= n ; ++ i){
        if(i % 2 == 1){
            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 main(){
    while (cin >> n && n){
        ton(i , n)
            cin >> r[i] ;
        if(n == 1){
            cout << r[1] << endl ;
            continue ;          //如果n == 1,则这个人要几个礼物就得准备几个礼物
        }
        r[n + 1] = r[1] ;       //行成环,最后一个人和第一个人相邻
        int L = 0 , R = 0 ;
        ton(i , n)
            L = max(L , r[i] + r[i + 1]) ;  //找到需要准备的礼物的最小值
        if(n % 2 == 1){     //如果是奇数,则二分查找,如果是偶数,则上面的L就是结果
            ton(i , n)
                R = max(R , 3 * r[i]) ;     //找到需要准备的最大值(实际不可能达到)
            while (L < R){      //二分查找,找到合适的值,存放在L中
                int mid = L + (R - L) / 2 ;
                if(check(mid))
                    R = mid ;
                else
                    L = mid + 1 ;
            }
        }
        cout << L << endl ;     //输出结果
    }
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值