题解 UVA1335 【Beijing Guards】

UVA1335 Beijing Guards

双倍经验:P4409 [ZJOI2006]皇帝的烦恼

如果只是一条链,第一个护卫不与最后一个护卫相邻,那么直接贪心,找出最大的相邻数的和。

当变成环,贪心很容易找到反例(如[5,5,5],贪心答案为10,实际上为15)

答案存在单调性,考虑二分

那么怎么判断mid是否合理呢?设mx【i】表示第i个数与第一个数最多可以相同多少个,mn【i】 表示第i个数与第一个数至少相同多少个

mx[i]=min(a[i],a[1]-mn[i-1]);

【i-1】最少有mn【i-1】个数与【1】相同,则【i】一定不包括这些数

mn[i]=max(0,a[1]+a[i-1]-mx[i-1]+a[i]-x);

a[i-1]-mx[i-1]是【i-1】至少与【1】不同的个数,加上a【1】是【1】与【i-1】所用的最少个数,那么对于【i】,必须有a[1]+a[i-1]-mx[i-1]+a[i]-x个数与【1】相同才能满足x的限制

那么如果mn【n】==0则说明a[1]+a[i-1]-mx[i-1]+a[i]-x<=0,x可以调小

当n=1直接输出答案

#include<cstdio>
#include<iostream>
using namespace std;
#define R register int
int a[100001],mx[100001],mn[100001],n,l,r,mid;
inline bool check(int x) {
	for (R i=2; i<=n; i++) {
		mx[i]=min(a[i],a[1]-mn[i-1]);
		mn[i]=max(0,a[1]+a[i-1]-mx[i-1]+a[i]-x);
	}
	return !mn[n];
}
int main() {
	while(scanf("%d",&n) && n) {
		l=0;
		for (R i=1; i<=n; i++) scanf("%d",&a[i]),l=max(l,a[i]+a[i-1]);
		if(n==1) {
			printf("%d\n",a[1]);
			continue;
		}
		for (mx[1]=mn[1]=a[1],r=300000; l<=r;)
			if (check(mid=(l+r)>>1)) r=mid-1;
			else l=mid+1;
		printf("%d\n",l);
	}
}
posted @ 2019-08-31 17:19  Randolph、  阅读(110)  评论(0编辑  收藏  举报