图片加载可能有点慢,请跳过题面先看题解,谢谢
Uva的题目还是很好的,比如这道,是一道比较好的思维题,代码难度不大
首先处理一下偶数的情况,很简单,答案是相邻两个守卫的礼物和的最大值
这儿请 fhr 给出证明:
把物品排成一行,只要一个人从左边开始取,下一个人从右边开始取,以此类推,保证不会重复
fhr 这人平时虽然脑子不太好使,但这个证明还是不错的。
那么奇数的情况怎么办呢,有一种策略是这样的:
假设有 \(cnt\) 种物品,第一个人取第 \(1~a[1]\) 个,往后走,编号是偶数的人从前往后取,奇数从后往前取,中途必须保证相邻两人取的东西不同。
举个栗子:
就拿样例的第二组数据来说,\(a[]={2,2,2,2,2}\),然后 \(cnt=5\) 。
在这样的情况下,每个人取物品的情况分别为 {1,2},{3,4},{5,2},{1,3},{5,4}。
这个东西啊看起来不那么好处理,但是请注意,这道题是不需要输出方案的,所以我们可以这样:
\(cnt\) 可以通过二分得到,
记 \(l[i]\),\(r[i]\),表示第 \(i\) 个人,在 \(1~a[1]\) 和 \(a[1]+1~cnt\) 各取了多少件,显然 \(l[1]=a[1]\)。
那么这样,每个人都可以根据前一个人的 \(l\) 和 \(r\) 值算出自己的 \(l\) 和 \(r\),判一下是否合法就行。
//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }
int n,ans,a[100010];
int l[100010],r[100010];
il void init(){ ans=0;
for(RG int i=1;i<=n;i++) a[i]=gi();
for(RG int i=2;i<=n;i++) ans=max(ans,a[i]+a[i-1]);
ans=max(ans,a[1]+a[n]);
}
il bool ck(int x){
RG int k=x-a[1]; l[1]=a[1];
for(RG int i=2;i<=n;i++){
if(!(i%2)) l[i]=min(a[i],a[1]-l[i-1]),r[i]=a[i]-l[i];
else r[i]=min(a[i],k-r[i-1]),l[i]=a[i]-r[i];
if(l[i]+r[i]>x) return 0;
if(l[i]<0||r[i]<0) return 0;
}
if(l[n]>0) return 0;
return 1;
}
il void work(){
if(n==1){ printf("%d\n",a[1]); return ; }
if(!(n%2)){ printf("%d\n",ans); return ; }
RG int l=ans,r=l<<1;
while(l<=r){
RG int mid=(l+r)>>1;
if(ck(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
}
int main(){ while(scanf("%d",&n)&&n){ init(); work(); } return 0; }