UVA 1335 - 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个人围成一个环,其中第i个人想要a[i]种不同的礼物,相邻的两个人可以聊天,炫耀自己的礼物。如果两个相邻的人拥有相同的某种礼物,双方都会不高兴,问最少需要多少种不同的礼物才能使所有人都开心(每种礼物有无限个)。

思路:

  1. 如果n=1,那么答案就为a[1]。

  2. 如果n为偶数,易得到答案为相邻两个数之和最大的那个值,即ans=max(a[i],a[i-1])。

  3. 如果n为奇数,这时比较复杂。这里用了一个非常巧妙的方法:

    假设第一个人取了前x个,即取了编号为1~x的礼物,那么偶数编号的人就尽量从前取,奇数编号的人尽量从后取,这样能最大限度的避免冲突。
    例如:
    当n=7,礼物数=8,a[]={2,5,3,4,2,5,2}时,第一个人拿1,2号礼物,第二个人拿3,4,5,6,7号礼物,第三个人拿8,2,1号礼物,第四个人拿3,4,5,6号礼物,第五个人拿8,7号礼物,第六个人拿1,2,3,4,5号礼物,第七个人拿8,7号礼物。

如何划分从左取和从右取边界呢?我们可以以第一个人拿的礼物数a1为边界,即分为1—a1,a1+1—p两个,偶数从1—a1取,不足时再从a1+1—p取,奇数同理。这样分的原因是当我们
判断到第n个人时,如果从1—a1取了,那么p就不符合条件。

我们可以二分查找最小的符合条件的礼物数,二分的下限为相邻两个数之和最大的那个值,二分的上限为最大值的三倍。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int a[100009],left1[100009],right1[100009],n;//left1[i]表示第i个人从左面取的礼物数,right1[i]同理
int judge(int p)
{
	int x,y;
	left1[1]=a[1];
	right1[1]=0;
	x=a[1],y=p-a[1];
	for(int i=2;i<=n;i++)
	{
		if(i%2)
		{
			right1[i]=min(a[i],y-right1[i-1]);
			left1[i]=a[i]-right1[i];
		}
		else
		{
			left1[i]=min(a[i],x-left1[i-1]);
			right1[i]=a[i]-left1[i];
		}
	}
	return left1[n]==0;
}
int main()
{
	int maxx,mmax;
	while(~scanf("%d",&n)&&n)
	{
		maxx=0;mmax=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			if(i>1)
				maxx=max(maxx,a[i]+a[i-1]);
			mmax=max(mmax,a[i]*3);
		}
		if(n==1)
		{
			cout<<a[1]<<endl;
			continue;
		}
		a[n+1]=a[1];
		maxx=max(maxx,a[1]+a[n]);
		if(n%2==0)
		{
			cout<<maxx<<endl;
			continue;
		}
		else
		{
			int l,r,mid;
			l=maxx,r=mmax;
			while(l<=r)
			{
				mid=(l+r)/2;
				if(judge(mid))
					r=mid-1;
				else
					l=mid+1;
			}
			cout<<r+1<<endl;
			continue;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值