AYITACM2016省赛第三周M - Beijing Guards(贪心+二分)

Beijing was once surrounded by four rings of city walls: the Forbidden City Wall, the Imperial City
Wall, the Inner City Wall, and finally the Outer City Wall. Most of these walls were demolished in
the 50s and 60s to make way for roads. The walls were protected by guard towers, and there was a
guard living in each tower. The wall can be considered to be a large ring, where every guard tower has
exaetly two neighbors.
The guard had to keep an eye on his section of the wall all day, so he had to stay in the tower.
This is a very boring job, thus it is important to keep the guards motivated. The best way to motivate
a guard is to give him lots of awards. There are several different types of awards that can be given:
the Distinguished Service Award, the Nicest Uniform Award, the Master Guard Award, the Superior
Eyesight Award, etc. The Central Department of City Guards determined how many awards have to
be given to each of the guards. An award can be given to more than one guard. However, you have
to pay attention to one thing: you should not give the same award to two neighbors, since a guard
cannot be proud of his award if his neighbor already has this award. The task is to write a program
that determines how many different types of awards are required to keep all the guards motivated.
Input
The input contains several blocks of test eases. Each case begins with a line containing a single integer
l ≤ n ≤ 100000, the number of guard towers. The next n lines correspond to the n guards: each line
contains an integer, the number of awards the guard requires. Each guard requires at least 1, and at
most l00000 awards. Guard i and i + 1 are neighbors, they cannot receive the same award. The first
guard and the last guard are also neighbors.
The input is terminated by a block with n = 0.
Output
For each test case, you have to output a line containing a single integer, the minimum number x of
award types that allows us to motivate the guards. That is, if we have x types of awards, then we can
give as many awards to each guard as he requires, and we can do it in such a way that the same type
of award is not given to neighboring guards. A guard can receive only one award from each type.
Sample Input
3
4
2
2
5
2
2
2
2
2
5
1
1
1
1
1
0
Sample Output
8
5
3

题意:

n个人,每个人都想要礼物,相邻两个人会聊天炫耀礼物,不能让他们礼物相同,问至少要多少个礼物才能满足条件。

思路:

偶数个人的话,礼物个数即为最大的两个邻居想要礼物数之和。

奇数个人的话比较复杂,二分搜索最小礼物个数p,将礼物分为左右两堆,左堆放x,右堆y(x+y=p),left[i],right[i]分别表示第i个人拿走了左,右两堆多少个礼物,让奇数号的人尽量从右边拿,偶数号的人尽量从左边拿,第一个人拿走了左边所有的x,最后只要再判断一下最后一个人是否拿过左边的礼物就行。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
using namespace std;
int a[100010],left[100010],right[100010],n;
int judge(int p)
{
    int x,y,i;
    left[1]=a[1],right[1]=0;
    x=a[1],y=p-a[1];//将礼物分成两堆
    for(i=2; i<=n; i++)
    {
        if(i%2==0)//如果是第偶数个保安
        {
            left[i]=min(a[i],x-left[i-1]);//尽量从左边拿,判断上一个人留下的礼物是否够,如果不够再拿右边
            right[i]=a[i]-left[i];//记录在右边拿了几个礼物
        }
        else
        {
            right[i]=min(a[i],y-right[i-1]);//尽量从右边拿,判断目前礼物是否够
            left[i]=a[i]-right[i]; //记录还需从右边拿几个
        }
    }
    return left[n]==0;//判断最后一个人是否拿过左边的礼物
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        int i,l=0,r=0,m;//l为二分的下限,r为上限
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            r=max(r,a[i]*3);//r最大为需要礼物最多的保安的三倍,
        }
        if(n==1)//如果只有一个保安,礼物数量就是他需要的数量
        {
            printf("%d\n",a[1]);
            continue;
        }
        a[n+1]=a[1];//因为是环形,所以需要考虑最后一个和第一个的关系
        for(i=1; i<=n; i++)
            l=max(l,a[i]+a[i+1]);//二分下限为相邻的保安礼物之和的最大值
        if(n%2==1)  //如果保安数量为奇数
        {
            while(l<=r)   //e二分求最优解
            {
                m=(l+r)/2;
                if(judge(m))  //如果礼物够分,就再减少一个礼物
                    r=m-1;
                else   //如果礼物不够分,就增加礼物
                    l=m+1;
            }
            printf("%d\n",r+1);//最后输出礼物数量
        }
        else //如果保安数量为偶数,最优解就是相邻保安礼物之和的最大值
            printf("%d\n",l);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值