• [ HAOI 2011 ] Problem A


    (\)

    (Description)


    共有(n)个人排序,第(i)个人说有(a_i)个人比他高,(b_i)个人比他低,问最少说谎人数(可能有相同的权值)。

    • (Nin [1,10^5])(a_i,b_i in [1,N])

    (\)

    (Solution)


    • 转化问题,假设我们已经有排好序的降序序列,若第(i)个人说的是真话,则证明在位置在区间([a_i+1,n-b_i])内的数应该是等大的,有显然可以去掉不合法的部分是(a_i+b_ige n)的人。
    • 同时注意到覆盖区间相同的可以同时存在,但假设相同的区间为([l,r]),那么保留的人数不能超过(r-l+1)个人。这一步去除不合法的人可以通过双关键字排序后扫描一遍即可,此时我们把合法的同一个区间的人放在一起,人数记为这个区间的价值,注意,这个人数最多只能是(r-l+1)
    • 问题转化为,有(N)个线段,选出互不相交的一个线段集合,使得集合内的线段权值和最大。这个权值和即为最多的说实话的人,容斥一下用(n)减掉这个数就是答案。
    • 这一部分因为引入了权值所以不能简单的右端点排序贪心,而要(DP)。先将线段按右端点排序,保证转移考虑了所有前面的线段,设(f[i])为第(i)个线段及以前的所有线段能产生的最大答案,有显然的(N^2)转移是(egin{align}f[i]=max(max{f[j] ig| j<i},max{f[k]+val[i] ig | r[k]<l[i]})}end{align}),考虑减少转移数,第一部分显然是一路取(max)过来所以可以直接化简为(f[i-1]),而第二部分可以直接化简为只找最大的合法的(k),因为再往前的部分第一个(max)已经辅助完成了,这个过程可以通过二分查找实现。
    • 因为答案是单调不降的,所以答案即为(n-f[tot])

    (\)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 100010
    #define R register
    #define gc getchar
    using namespace std;
    
    int f[N];
    struct lim{int l,r;}s[N];
    struct seg{int l,r,val;}p[N];
    
    inline int rd(){
      int x=0; bool f=0; char c=gc();
      while(!isdigit(c)){if(c=='-')f=1;c=gc();}
      while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
      return f?-x:x;
    }
    
    inline bool cmp1(lim x,lim y){return x.l==y.l?x.r<y.r:x.l<y.l;}
    
    inline bool cmp2(seg x,seg y){return x.r==y.r?x.l<y.l:x.r<y.r;}
    
    inline int find(int x){
      int l=0,r=x-1;
      while(l<r){
        int mid=((l+r+1)>>1);
        if(p[mid].r<p[x].l) l=mid;
        else r=mid-1;
      }
      return l;
    }
    
    int main(){
      int n=rd(),tot1=0,tot2=0;
      for(R int i=1,l,r;i<=n;++i){
        l=rd(); r=rd();
        if(l+r<n){s[++tot1].l=l+1;s[tot1].r=n-r;}
      }
      sort(s+1,s+1+tot1,cmp1);
      for(R int i=1,cnt;i<=tot1;++i){
        cnt=1;
        while(s[i+1].l==s[i].l&&s[i+1].r==s[i].r) ++cnt,++i;
        cnt=min(cnt,s[i].r-s[i].l+1);
        p[++tot2].l=s[i].l; p[tot2].r=s[i].r; p[tot2].val=cnt;
      }
      sort(p+1,p+1+tot2,cmp2);
      for(R int i=1;i<=tot2;++i)
        f[i]=max(f[i-1],f[find(i)]+p[i].val);
      printf("%d
    ",n-f[tot2]);
      return 0;
    }
    
    
  • 相关阅读:
    47.一个h5画板
    46.纯css实现瀑布流(flex)
    45.使用webpack,react,redux搭俩个界面
    44.一个这样的datePicker
    43.关于浮点数的乘除法预算误差,一个看似没什么问题的解决方法
    iOS10 NSBundle
    iOS09-NSUserDefault
    iOS08 页面跳转动画设置方法
    iOS07 键盘弹出,回收,位置
    iOS06 NSTimer 详细设置1
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9591966.html
Copyright © 2020-2023  润新知