LA3177 Beijing Guards --- 贪心(经典题)

题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1178

题意: 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值