zoukankan      html  css  js  c++  java
  • [ TJOI 2013 ] 拯救小矮人

    (\)

    (Description)


    (N)个人,每个人有两个属性 (A_i,B_i) ,现在他们要从洞里逃出去。

    (x) 个人能逃出去的条件是,随意选择一些还在洞里的人(()重新编号为(1...k)),满足

    [A_1+A_2+...+A_k+A_x+B_xge H ]

    注意,第 (x) 个人不能在这 (k) 个人里。求最多能跑出去多少个人。

    • (Nle 2000 , A_i,B_i,Hle10^5)

    (\)

    (Solution)


    贪心和 (DP) 的结合,不得不说是一道好题。

    首先考虑能否直接贪心。

    考虑如果直接按照 (A) 从大到小排个序,然后强制留下来前面的一些使得满足出洞条件,然后让其他的人都出去是否是最优解。

    并不是。如果把 (A) 比作身高,把 (B) 比作手长,那可能有一些身子又高手又长的人,用一些身子一般高但是手极短的人换掉,可能能逃跑的人更多。

    (\)

    然后就有了比较厉害的 (DP​) 解法。

    首先考虑两个人 (x,y) 他们如果要出洞,应该以什么顺序出。

    答案是 (A+B) 较小的那个人先出。

    证明分三种情况讨论。

    • 如果只能走一个,也就是说少了两人之一另一个都走不了,那谁都无所谓。
    • 如果都走不了,显然顺序无关。
    • 如果两个在相同的条件下都能走,那我们先让小的走,因为身子高手长的更好逃跑。

    然后就可以按照 (A+B) 排序了。

    (\)

    然后设状态 (f[i]) 表示 出去了 (i) 个人,剩下的人 的身高之和最多是多少。

    然后状态之间的转移就代表出去了一个人。我们要保证转移合法,一定要满足:

    • 一个人不能出洞多次(在多次转移里使用同一个人)
    • 出洞的这个人一定在之前的集合里

    然后解决这个问题的方法就比较妙了。

    我们显然不能在外层枚举出去的人数,然后内层枚举出去的人,这样两个问题都无法避免。

    采取一种类似背包的思路。外层枚举出去的人,内层枚举要更新的层数。

    为了满足第一个限制条件,我们要像 (01) 背包那样更新,保证一个人只被使用一次。

    为了满足第二个限制条件,我们要动态控制状态的上限。

    (\)

    先放代码。

    f[ans=0]=sum;
    for(R int i=1;i<=n;++i){
      for(R int j=ans;j>=0;--j)
        if(f[j]+p[i].b>=m) f[j+1]=max(f[j+1],f[j]-p[i].a);
      if(f[ans+1]>=0) ++ans;
    }
    

    外层循环是考虑哪个人出去,内层循环是要更新的层数,内层倒序的原理上面说过了。

    转移条件挺显然的吧,就是合法就把他拿出去,然后尝试更新。

    为什么一定能拿出去?因为这个位置有值,一定是从更低的转移过来,而更低的拿出去的只是当前这个人之前的人,注意体会这个 (ans) 的作用。

    (\)

    (Code)


    #include<cmath>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 2010
    #define R register
    #define gc getchar
    #define inf 2000000000
    using namespace std;
    
    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;
    }
    
    int n,m,ans,f[N];
    
    struct prs{int a,b;}p[N];
    
    inline bool cmp(prs x,prs y){return x.a+x.b<y.a+y.b;}
    
    int main(){
      n=rd();
      for(R int i=1;i<=n;++i){
        f[0]+=(p[i].a=rd());
        p[i].b=rd(); f[i]=-inf;
      }
      m=rd();
      sort(p+1,p+1+n,cmp);
      for(R int i=1;i<=n;++i){
        for(R int j=ans;j>=0;--j)
          if(f[j]+p[i].b>=m) f[j+1]=max(f[j+1],f[j]-p[i].a);
        if(f[ans+1]>=0) ++ans;
      }
      printf("%d
    ",ans);
      return 0;
    }
    
    
  • 相关阅读:
    游标cursor
    SQL: EXISTS
    LeetCode Reverse Integer
    LeetCode Same Tree
    LeetCode Maximum Depth of Binary Tree
    LeetCode 3Sum Closest
    LeetCode Linked List Cycle
    LeetCode Best Time to Buy and Sell Stock II
    LeetCode Balanced Binary Tree
    LeetCode Validate Binary Search Tree
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9811832.html
Copyright © 2011-2022 走看看