二分模型的探究

nyoj 229 工程

http://acm.nyist.net/JudgeOnline/problem.php?pid=229
描述
有n个工人做两个工程A和B,每个工程都被分为相同的m份,给你第i个工人做A中的一份需要的时间Xi秒,和做B中的一份所需时间Yi秒,问最短需要多少时间可以完成这两项工程。
输入
第一行是一个整数t (1 <= t <= 100),表示有t组测试数据;
每组测试数据第一行有两个整数 n (1 <= n <= 100), m (1 <= m <= 100).
接下来的n行,每行有两个整数Xi,Yi;
输出
输出最短时间,占一行。
样例输入
1
3 20
1 1
2 4
1 6
样例输出
18

   二分模型。最关键的是全局的思想。

  此题的模型 。

  1、 求最短时间。

  2、单调性。  给的时间越长,越易完成。

               这个函数模型为。  f(x)  = 0   (x < T)  

                                              f(x)  = 1   (x >= T)

             这个分段函数为单调函数(非严格递增)。

  3、 judge 函数也就是2中的f(x)的编写 。 背包。 


const int Max_N = 108 ;
int   dp[Max_N] ;
int   A[Max_N] , B[Max_N] ;
int   N , M ;

int   DP(int Time){   //在时间为Time的情况下能否搞定
      int man , i , j , k  ;
      memset(dp , -1 , sizeof(dp)) ;
      for(i = 0 ; i <= M && i*A[1] <= Time ; i++)   //第一个人做i个A,还可以做dp[i]个B
          dp[i] = (Time-i*A[1])/B[1] ;
      if(dp[M] >= M)
          return  1 ;
      for(man = 2 ; man <= N ; man++){     
          for(i = M ; i >= 0 ; i--){    //从大到小更新,联想尺子
              for(j = 0 ; j<=i && j*A[man] <= Time ;j++){   //从小到大,是为了满足i从大到小。
                 if(dp[i-j] != -1)
                     dp[i] = max(dp[i] , dp[i-j] + (Time-j*A[man])/B[man]) ;
              }
          }
      }
      return  dp[M] >= M ;
}

int  Ans(int up){    //二分枚举
     int Left = 1 ;
     int Right = up  ;
     int Mid  , ans ;
     while(Left <= Right){
          Mid = (Left + Right) >> 1 ;
          if(DP(Mid)){
              ans = Mid ;
              Right = Mid - 1 ;
          }
          else
              Left = Mid + 1 ;
     }
     return ans ;
}

int  main(){
     int i , t , _up ;
     scanf("%d" ,&t) ;
     while(t--){
          scanf("%d%d" ,&N ,&M) ;
          _up = 0 ;
          for(i = 1 ; i <= N ; i++){
              scanf("%d%d" ,&A[i] , &B[i]) ;
              _up = max(_up , A[i]) ;
              _up = max(_up , B[i]) ;
          }
          printf("%d\n" , Ans(_up*M*2)) ;
     }
     return 0 ;
}
     

nyoj 804   Gift

http://acm.nyist.net/JudgeOnline/problem.php?pid=804

  题意  :  N种珠子, 每种x[i] 个 。  要组成长M的项链,使得每条项链珠子都不用 。 求最多能组成多少条这样的项链。

  1、 求最大值。

  2 、单调性。记  f(x) =  1   可以组成x个 。

                              f(x) = 0   不能组成x个。 

                      对任意的x  < y    ,有 f(x) >= f(y)  

3、judge(x)函数  。

       组成x条项链,  对于每一种珠子, 最多出现x个 (也就是没条项链都参与)。

        一共可以参与的珠子个数从上到下, 从做到右平均分配到x条项链中,判断长度是否>=M。


const int Max_N = 1008 ;
int   N  , M ;
int   x[Max_N] ;

int   judge(int  k){
      int sum = 0 ;
      for(int i = 1 ; i <= N ; i++)
          sum +=  x[i] >= k ?  k : x[i] ;
      return sum/M >= k ;
}

int   Ans(int up){
      int  Left = 0 ;
      int  Right = up ;
      int  Mid , ans ;
      while(Left <= Right){
           Mid = (Left + Right) >> 1 ;
           if(judge(Mid)){
               ans = Mid ;
               Left = Mid + 1 ;
           }
           else
               Right = Mid - 1 ;
      }
      return ans ;
}

int  main(){
     int i  , sum ;
     while(scanf("%d" ,&N) && N){
          sum = 0 ;
          for(i = 1 ; i <= N ; i++){
              scanf("%d" ,&x[i]) ;
              sum += x[i] ;
          }
          scanf("%d" ,&M) ;
          printf("%d\n" , Ans(sum/M)) ;
     }
     return 0 ;
}

nyoj 586 疯牛

描述
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,...,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入
有多组测试数据,以EOF结束。
第一行:空格分隔的两个整数N和C
第二行——第N+1行:分别指出了xi的位置
输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1
2
8
4
9
样例输出
3

本题是很经典的 青蛙过河 模型。


const int Max_N = 100008 ;
int   N  , M ;
int   x[Max_N] ;

int  judge(int Len){
     int dot = 1 , now = x[1] , i ;
     for(i = 2 ; i <= N ; i++){
         if(x[i] - now < Len)
            continue ;
         else{
            now = x[i] ;
            dot++ ;
            if(dot >= M)
                return 1 ;
         }
     }
     return  dot >= M ;
}

int  Ans(int up){
     int Left = 0 ;
     int Right = up ;
     int Mid , ans ;
     while(Left <= Right){
          Mid = (Left + Right) >> 1 ;
          if(judge(Mid)){
               ans = Mid  ;
               Left = Mid + 1 ;
          }
          else
               Right = Mid - 1 ;
     }
     return ans ;
}

int  main(){
     int i  , sum ;
     while(scanf("%d%d" ,&N ,&M) != EOF){
          for(i = 1 ; i <= N ; i++)
              x[i] = getint() ;
          sort(x+1 , x+1+N) ;
          printf("%d\n" , Ans(x[N] - x[1])) ;
     }
     return 0 ;
}



关于double二分,奔跑的xiaodao

 

某天,DS同学和他的妹子终于要见面了。DS在遥远的西藏,妹子在北京,中间隔着一条长长的川藏公路。DS和妹子都在这条公路上相向而行,因为过于思念对方,DS派出了xiaodao作为自己的情书信使。

DS和妹子相向而行,速度为 v1 , v2 m/s 尽职尽责的xiaodao同学以 v m/s 的速度奔跑,他一开始拿着DS的信向妹子的方向狂奔,遇到妹子之后毫不停歇,拿着妹子的书信继续以 v 的速度向DS奔跑,周而复始,一直到DS和妹子相遇为止。好辛劳的xiaodao

但是DS是个胖子,妹子是女生,大家都体力不太行。已知DS奔跑 T1 s 之后就要休息 Wait1 s ,妹子奔跑 T2 s 之后就要休息 Wait2 s 。而xiaodao是不会休息的!经过计算,DS和妹子的初始距离为 L 

xiaodao想问你,当DS和妹子终于相遇的时候,xiaodao这时候已经奔跑了多少 m 的距离。

Input

第一行一个整数 T  代表数据组数,以下 T 组数据。

每组数据包含 8 个实数 分别代表 v1 , v2 , v , T1 , T2 , Wait1 , Wait2 , L

1 <= v1 , v2 <= 100 , v1 < v <= 100 , 1 <= T1 , T2 <= 1000 , 0 <= Wait1 , Wait2 <= 1000 . 1 <= L <= 10000 .

Output

对于每组数据输出一个实数 S 代表 xiaodao 奔跑的距离。
如果标程给出的答案是 Ans  只要 | Ans - S | < 1e-5 你就会得到 Accepted

Sample Input
1
1.00 1.00 2.00 1.00 1.00 0.00 0.00 2.00
Sample Output
2.000000000
Source
哈尔滨理工大学第四届ACM程序设计竞赛(同步赛)


typedef long long LL ;
typedef double LD ;
#define eps 1e-8

LD  t1 ,wait1 , t2 ,wait2 , v1 , v2;
LD  Lenth ;

int  judge(LD  Time){
     LD T , len1 , len2 , t ;
     LL tt1 = floor(Time/(t1+wait1)) ;
     len1 = v1*tt1*t1 ;
     if(Time - tt1*(t1+wait1) >= t1)
        t = t1 ;
     else
        t = Time - tt1*(t1+wait1) ;
     len1 += v1*t ;


     LL tt2 = floor(Time/(t2+wait2)) ;
     len2 =  v2*tt2*t2 ;
     if(Time - tt2*(t2+wait2) >= t2)
        t = t2 ;
     else
        t = Time - tt2*(t2+wait2) ;
     len2 += v2*t ;

     if(len1 + len2 > Lenth || fabs(len1 + len2 - Lenth) < eps)
          return 1 ;
     return 0 ;
}

LD  b_s(){
        LD L , R , Mid , ans ;
        L =  0.0 ;
        R =  1e8 ;
        while(L + eps < R){
             Mid = (L + R) * 0.5 ;
             if(judge(Mid)){
                  ans = Mid ;
                  R = Mid  ;
             }
             else
                  L = Mid  ;
        }
        return ans ;
}

LD v ;

int  main(){
     int t ;
     cin>>t ;
     while(t--){
           scanf("%lf%lf%lf%lf%lf%lf%lf%lf" , &v1 ,&v2 ,&v ,&t1,&t2,&wait1,&wait2,&Lenth) ;
           printf("%.9lf\n",b_s() *v) ;
     }
     return 0 ;
}


给出农夫在n天中每天的花费,要求把这n天分作m组,每组的天数必然是连续的,要求分得各组的花费之和应该尽可能地小,最后输出各组花费之和中的最大值

const  int  maxn = 100008 ;
int    x[maxn]  , m , n ;
int    judge(int c){
       int s = 1  , t = 0 ;
       int i = 1 ;
       while(i <= n){
           if(t + x[i] > c){
                s++ ;
                t = x[i] ;
           }
           else  t += x[i] ;
           i++ ;
       }
       return s <= m ;
}

int    main(){
       int i , L , R , M  , s ;
       while(cin>>n>>m){
            R = L = 0  ;
            for(i = 1 ; i <= n ; i++){
                scanf("%d" ,&x[i]) ;
                R += x[i] ;
                L = max(L , x[i]) ;
            }
            while(L <= R){
                M = (L + R) >> 1 ;
                if(judge(M)){
                     s = M ;
                     R = M - 1  ;
                }
                else L = M + 1 ;
            }
            printf("%d\n" , s) ;
       }
       return  0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值