zoukankan      html  css  js  c++  java
  • USACO 2017 December Contest Platinum T3: Greedy Gift Takers

    题目大意

    有 N(1N1e5)头牛按顺序排成一列,编号从 1 到 N,1 号牛在队头,N 号牛在队尾。

    每次位于队头的牛 i 拿到一个礼物,然后插入到从队尾数ci头牛之前的位置。。举个栗子: 初始队列 1,2,3,4,5 c1= 2,c2 = 3,则第一次操作后的序列为 2,3,1,4,5,第二次操作后的序列为 3,2,1,4,5。重复无限次操作,求最后有几头牛拿不到礼物。

    题目分析

    一上来有个显然的结论,若一个人得不到礼物那么原序列中在他后面的人肯定也得不到礼物,因为后面的人跳不到前面来。

    所以我们只要找到第一个拿不到礼物的人,计算一下就是答案。

    也就是说,我们要找到最后一个能拿到礼物的人。

    显然,这个具有单调性,可以二分。

    二分可以拿到礼物的人数,那么怎么判断可不可行呢?

    我们要判断在 mid之前 是否形成了一个循环即可。若形成了循环,则mid不能拿到礼物。

    怎么判断循环呢,出现在前i个位置的牛有多于i头,则这i头牛就会一直卡在这前i个位置。

    注意,Check时要把 c[1] ~ c[mid-1] 排序,因为c大的拿礼物一次后可能还会跳到mid前面,而c小的就永远留在后面了,这样相当于在 “c较大的牛“ 的c上减了1。

    而让c小的全跳到后面去而不构成循环,才是我们真正要判断的。

     (不理解的可以对着代码仔细想一下)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int MAXN=1e5+10;
     4 
     5 int n,ans;
     6 int c[MAXN],num[MAXN];
     7 inline bool Check(int mid){
     8     if(mid==1) return true;
     9     for(int i=1;i<mid;++i) num[i]=c[i];
    10     sort(num+1,num+mid);
    11     int lim=n-mid;
    12     for(int i=1;i<mid;++i){
    13         if(num[i]>lim) return false;
    14         ++lim;
    15     }
    16     return true;
    17 }
    18 int main(){
    19     scanf("%d",&n);
    20     for(int i=1;i<=n;++i)
    21         scanf("%d",&c[i]);
    22     int l=1,r=n+1;
    23     while(l<=r){
    24         int mid=(l+r)>>1;
    25         if(Check(mid)){
    26             ans=mid;l=mid+1;
    27         }
    28         else
    29             r=mid-1;
    30     }
    31     printf("%d
    ",n-ans);
    32     return 0;
    33 }
  • 相关阅读:
    CompletionService--实现并行获取future.get()结果
    ConcurrentHashMap+FutureTask实现高效缓存耗时较长的业务结果获取
    Exchanger--线程执行过程中交换数据
    线程池的处理流程
    CyclicBarrier--栅栏,挡住指定数量线程后一次放行
    Semaphore--信号量,控制并发线程数,如流量控制
    CountDownLatch---多线程等待
    线程安全相关概念
    SimpleDateFormat非线程安全
    记java.lang.NoSuchMethodError错误解决过程
  • 原文地址:https://www.cnblogs.com/LI-dox/p/11235645.html
Copyright © 2011-2022 走看看