[UVALive 3177] Beijing Guards

图片加载可能有点慢,请跳过题面先看题解,谢谢
1196604-20171011170441527-1182123794.png
1196604-20171011170446559-1847800852.png
1196604-20171011170450840-1688423405.png
1196604-20171011170455199-188206722.png

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; }

转载于:https://www.cnblogs.com/Hero-of-someone/p/7651589.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值