• UVALive 3177 Beijing Guards


    题目大意:给定一个环,每个人要得到Needi种物品,相邻的人之间不能得到相同的,问至少需要几种。

    首先把n=1特判掉。

    然后在n为偶数的时候,答案就是max(Needi+Needi+1)(包括(1,n))。

    证明:把物品排成一行,只要一个人从左边开始取,下一个人从右边开始取,以此类推,保证不会重复。

    然后在n为奇数的时候,答案就不好做了。

    观察一下这道题,发现答案满足可二分性,不如思考一下check怎么写。

    因为n为奇数时难点就在看n与1是否矛盾上,只要解决了这个问题,一切好说。

    不难发现答案下界就是max(Needi+Needi+1)(包括(1,n)),上界开大一点无所谓的。

    还是把物品排成一行,第一个人从左边开始取。

    要使第n个人和第1个人尽量不矛盾,不如让他从最后开始选。

    然后n-1就从前面开始选。

    一路逆推回来,你会发现:让2从左边开始选。

    什么?1不是从左边开始选的吗?为什么2也从左边开始选呢?

    因为我们二分的mid是可以保证(i,i+1)之间一定不矛盾的,所以从2开始做就是为了n号点与1号点的尽量不矛盾。

    换句话说,不会转过圈的都是一定满足的,所以就是要把它们以一种合适的方式排布,成全n和1这一对。

    感觉在下一盘惊天大棋?

    至于维护方式,也是比较巧(套)妙(路)的。

    有mid个物品,第一个人选了前Need[1]个,我们就把这些物品分成左右,[1,Need[1]]和[Need[1]+1,mid]。

    记L[i]和R[i]表示i在左边/右边取了多少个。

    对于偶数位,由推导,我们尽量从左边选。

    L[i]=min(Need[i],Need[1]-L[i-1]),R[i]=Need[i]-L[i]。

    对于奇数位,尽量从右边选。

    R[i]=min(Need[i],(mid-Need[1])-R[i-1]),L[i]=Need[i]-R[i]。

    最后,因为1把L全部取完了,我们只要看L[n]==0即可。

    真是妙。

    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring>
    #include    <queue>
    #include    <complex>
    #include    <stack>
    #define LL long long int
    #define dob double
    #define FILE "3177"
    using namespace std;
    
    const int N = 100010;
    int n,Ned[N],L[N],R[N];
    
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
    
    inline bool check(int mid){
      int Lh=Ned[1],Rh=mid-Ned[1];
      L[1]=Lh;R[1]=0;
      for(int i=2;i<=n;++i)
        if(i&1){
          R[i]=min(Ned[i],Rh-R[i-1]);
          L[i]=Ned[i]-R[i];
        }
        else{
          L[i]=min(Ned[i],Lh-L[i-1]);
          R[i]=Ned[i]-L[i];
        }
      return L[n]==0;
    }
    
    int main()
    {
      freopen(FILE".in","r",stdin);
      freopen(FILE".out","w",stdout);
      while(n=gi()){
        for(int i=1;i<=n;++i)Ned[i]=gi();
        if(n==1){printf("%d
    ",Ned[1]);continue;}
        int Ans=Ned[n]+Ned[1];
        for(int i=1;i<n;++i)
          Ans=max(Ans,Ned[i]+Ned[i+1]);
        if(n&1){
          int l=Ans,r=Ans*2;Ans=r;
          while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid))r=mid-1,Ans=mid;
            else l=mid+1;
          }
        }
        printf("%d
    ",Ans);
      }
      fclose(stdin);fclose(stdout);
      return 0;
    }
    Beijing Guards
  • 相关阅读:
    找出数组中最长的连续数字序列(JavaScript实现)
    从数组中选出和等于固定值的n个数(JavaScript实现)
    比较任意两个JSON串是否相等(比较对象是否相等)JAVA版
    freshcodecolor纯正则实现的在线代码着色(高亮)
    最新QQ强制聊天代码,同时可判断好友关系
    (转)spring异常抛出触发事务回滚策略
    (转)Java回收对象的标记 和 对象的二次标记过程
    (转)调用System.gc没有立即执行的解决方法
    java线程池与五种常用线程池策略使用与解析
    (转)Spring事务管理详解
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7651411.html
Copyright © 2020-2023  润新知