题解 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
#include
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);
    }
}

你可能感兴趣的:(题解 UVA1335 【Beijing Guards】)