二分与三分

二分题型最明显的有两种:求最大值的最小化,或最小值的最大化。

一般地,对于一个求最值的问题,如果不方便直接求解的话同时这个最值的趋向是单调的,就可以二分枚举。

此时二分一般加上dp或贪心的check()即可得知接下来的答案区间。


下面分别讨论最常见的两种。

 

 

最大值最小化:


对于此类问题,我们设答案的区间为[l,r]

那么当l=r时,显然区间只有一个值,这就是我们要的结果。

否则取mid=(l+r)/2,进行相应的check()操作,如果符合题意,那么表明这一设定的最大值有可能成为结果,我们需要比其更小的,所以r=mid,如果不符合题意,那么表明这一设定的最大值太小了且不可能成为结果,所以l=mid+1。

 

 

最小值最大化:

对于此类问题,我们设答案区间为[l,r)

显然当l+1=r是,区间只有一个值,这就是我们要的结果。

否则取mid=(l+r)/2,进行相应的check()操作,如果符合题意,那么表明这一设定的最小值有可能成为结果,我们需要更大的,所以l=mid,反之则表明这一设定的最小值不能够满足要求,我们就调小下一个mid,即r=mid(此时r为开区间,故取不到当前的mid).

模板

 

//求下界
{
    int l = 0, r = n, ans;
    int mid = (l + r) / 2;
    while(l >= r)
    {
        if(check(mid))    //check函数是判断该mid值是否满足条件
        {
            ans = mid;
            r = mid - 1; // 寻找比该mid更小的,左边界改变
        }
        else
            l = mid + 1;
        
    }
    
}

//求上界,原理和求下界一样
{
    int l = 0; r = n, ans;
    int mid = (l + r) / 2;
    while(l >= r)
    {
        if(check(mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
}
//二分查找/搜索,,   单调递增的情况下
{
    int x = X; //X是要查询的值
    int l = 0, r = n;
    int mid = (l + r) / 2;
    while(l >= r)
    {
        if(num[mid] == x) //num为存数的数组
            return mid;
        else
        {
            if(num[mid] > x)
                r = mid - 1;
            else
                l = mid + 1;
                
        }
    }
}

 

 

 

三分

二分法适用于求单调的时候用的,就比如说排序好的数组,那是递增的或者递减的。如果像出现了下图二次函数那样的怎么求他的最值呢?

 

        二分法早就失去了他的意义了。不过还是可以用三分法来实现的,就是二分中再来二分。比如我们定义了L和R,m = (L + R) / 2,mm = (mid + R) / 2; 如果mid靠近极值点,则R = mm;否则就是mm靠近极值点,则L = m;这样的话,极值还是可以求的。具体的还是看看题目吧。

 

zoj   3203   Light Bulb

--------------------------------------------------------------------------------

Time Limit: 1 Second      Memory Limit: 32768 KB

 

--------------------------------------------------------------------------------

Compared to wildleopard's wealthiness, his brother mildleopard is rather poor. His house is narrow and he has only one light bulb in his house. Every night, he is wandering in his incommodious house, thinking of how to earn more money. One day, he found that the length of his shadow was changing from time to time while walking between the light bulb and the wall of his house. A sudden thought ran through his mind and he wanted to know the maximum length of his shadow.

 

 

Input

 

The first line of the input contains an integer T (T <= 100), indicating the number of cases.

 

Each test case contains three real numbers H, h and D in one line. H is the height of the light bulb while h is the height of mildleopard. D is distance between the light bulb and the wall. All numbers are in range from 10-2 to 103, both inclusive, and H - h >= 10-2.

 

Output

 

For each test case, output the maximum length of mildleopard's shadow in one line, accurate up to three decimal places..

 

Sample Input

 

 

3

2 1 0.5

2 0.5 3

4 3 4

 

Sample Output

 

 

1.000

0.750

4.000

        题意很简单,就是人左右走动,求影子L的最长长度。

        由图可知,当人走进时,当影子刚好没有投到墙上的时候,是最长的。接着影子到了墙上就变小了,所以可以用三分法来求最值。当然用高数的求导还是可以解决的,只是定义域要注意下。

[html] view plain copy

  1. #include <stdio.h>  
  2.   
  3. int main()  
  4.   
  5. {   
  6.   
  7. int t;   
  8.   
  9. scanf("%d", &t);   
  10.   
  11. while(t--)   
  12.   
  13. {    
  14.   
  15.     double h, H, D;    
  16.   
  17.     scanf("%lf%lf%lf", &H, &h, &D);    
  18.   
  19.     double left = 0right = D * h / H;     
  20.   
  21.     int size = 100;    
  22.   
  23.     double mid, midmid, ans1, ans2;   
  24.   
  25.         while(size--)    
  26.   
  27.     {      
  28.   
  29.         mid = (left + right) / 2;      
  30.   
  31.         midmid = (mid + right) / 2;        
  32.   
  33.         ans1 = ((h*D-H*mid)/(H-h)*H)/((h*D-H*mid)/(H-h)+D)+mid;       
  34.   
  35.         ans2 = ((h*D-H*midmid)/(H-h)*H)/((h*D-H*midmid)/(H-h)+D)+midmid;      
  36.   
  37.         if(ans1 > ans2) right = midmid;       
  38.   
  39.             else left = mid;    
  40.   
  41.     }    
  42.   
  43.     printf("%.3lf\n", ans1);   
  44.   
  45.     }  
  46.   
  47. return 0;  
  48.   
  49. }  


 

        这个基本上是模版吧,只是那个函数要自己去写,只要解决了这个函数,就一直让他循环求极值吧,差不多100次就可以找到这个点了。

 

再来看一道题目

hdu  2438Turn the corner

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 404    Accepted Submission(s): 103

Problem Description
Mr. West bought a new car! So he is travelling around the city.One day he comes to a vertical corner. The street he is currently in has a width x, the street he wants to turn to has a width y. The car has alength l and a width d. Can Mr. West go across the corner?
 
Input
Every line has four real numbers, x, y, l and w. Proceed to the end of file.
 
Output
If he can go across the corner, print "yes". Print "no" otherwise.
 
Sample Input
10 6 13.5 4
10 6 14.5 4
 
 
Sample Output
yes
no

       

 

 

         其实就是汽车能不能拐弯的问题,告诉你X, Y, l, d判断是否能够拐弯,由下图可知,如果d大于X,Y的话,那么怎么样汽车也过不了。接着就是三分求下图那个最值了,如果到了这个地步,所求的h还是比Y小的话,那么肯定是可以拐弯的。

 

 

 

[html] view plain copy

  1. #include <cstdio>  
  2. #include <cmath>  
  3. const double pi = 3.1415926535;  
  4. int main()  
  5. {  
  6.       double x, y, l, w;  
  7.       while(scanf("%lf%lf%lf%lf", &x, &y, &l, &w) != EOF)  
  8.       {  
  9.             int size = 100;  
  10.             double left = 0right = pi / 2;  
  11.             double mid = 0midmid = 0, ans1, ans2;  
  12.             if(x < w || y < w){printf("no\n"); continue;}    
  13.             while(size--)  
  14.             {  
  15.                   mid = (left+right) / 2;  
  16.                   midmid = (mid+right) / 2;  
  17.                   ans1 = (x-l*sin(mid)-w/cos(mid)) / tan(mid);  
  18.                   ans2 = (x-l*sin(midmid)-w/cos(midmid)) / tan(midmid);  
  19.                   if(ans1 < ans2right = midmid;  
  20.                   else left = mid;  
  21.             }  
  22.             if(fabs(ans1) > y) printf("no\n");  
  23.             else printf("yes\n");  
  24.       }  
  25. return 0;  
  26. }  

 

这两题几乎就是用了三分的模版,然后就是简单的公式推导,然后就可以很方便的实现了,接着我们来看看稍微复杂点的吧。

xmu 1125.越野车大赛

Description

TheBeet正在参加一场越野车大赛。比赛的场地如右图:共分三块,每一块地面的长宽均为N与M,但地表情况不同,越野车在这段路面上的最高速度也不同。蓝色线表示TheBeet可能的行车路线。

比赛的要求是要求选手从比赛的场地左上角驾车至右下角。TheBeet想知道如果他在所有路段都以最快速度行驶(不考虑加速阶段),最快能在多少时间内完成比赛。

Input

  输入数据的第一行为两个正整数N M(N<=3000,M<=1000),表示一块路面的长和宽。
  第二行为三个正整数S1,S2,S3(0<S1,S2,S3<=100),从上至下依次表示各个路面上越野车的最高速度。

Output

  输出一个实数表示TheBeet最快能在多少时间内完成比赛。请输出一个尽可能精确的数字,控制误差在±0.000001的内。

Sample Input

30 10
2 5 3

Sample Output

13.7427361525

Hint

  如果你的输出和结果的相差在0.000001之内,则认为是正确答案。

 

        分析下该题,其实也是要求求最值,不过这个最值是满足两个地方的要求,想想和三分有什么关系呢?其实还是一样的,只是这里要用到两个三分了,不仅仅满足第一个要求,第二个要求也要一并给他满足了,接着这样的最值就是题目所要求的了。下面是搓搓的代码:

 

[html] view plain copy

  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cmath>  
  4. using namespace std;  
  5. const double pi = 3.141592654;  
  6.   
  7.   
  8.   
  9. inline double dis(double x1, double y1, double x2, double y2)  
  10. {  
  11.          return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));  
  12. }  
  13.   
  14.    
  15. int main()  
  16. {  
  17.          double n, m, a, b, c;  
  18.          while(cin >> n >> m)  
  19.          {  
  20.                   cin >> a >> b >> c;  
  21.                   double left1 = 0right1 = pi / 2;  
  22.                   double mid1, midmid1, mid2, midmid2, ans1, ans11, ans2, ans3, ans4, ans5, ans44, ans55;  
  23.                   int size = 30;  
  24.                   while(size--)  
  25.                   {  
  26.                            mid1 = (left1 + right1) / 2;  
  27.                            midmid1 = (mid1 + right1) / 2;  
  28.                            ans1 = dis((n - m / tan(mid1)), m, n, 0);  
  29.                            ans11 = dis((n - m / tan(midmid1)), m, n, 0);  
  30.                            int size1 = 30;  
  31.                            double left2 = 0right2 = pi / 2;  
  32.                            while(size1--)  
  33.                            {  
  34.                                     mid2 = (left2 + right2) / 2;  
  35.                                     midmid2 = (mid2 + right2) / 2;  
  36.                                     ans2 = dis((n - m / tan(mid1)), m, (n - m / tan(mid1) - m / tan(mid2)), 2*m);  
  37.                                     ans3 = dis((n - m / tan(mid1)), m, (n - m / tan(mid1)- m / tan(midmid2)), 2*m);  
  38.                                     ans4 = ans1 / c + ans2 / b + dis(0, 3*m, (n - m / tan(mid1) - m / tan(mid2)), 2*m) / a;  
  39.                                     ans5 = ans1 / c + ans3 / b + dis(0, 3*m, (n - m / tan(mid1) - m / tan(midmid2)), 2*m) / a;  
  40.                                     if(ans4 > ans5) left2 = mid2;  
  41.                                     else right2 = midmid2;  
  42.                            }  
  43.                              
  44.   
  45.             left2 = 0right2 = pi / 2;  
  46.                     size1 = 30;  
  47.                     while(size1--)  
  48.                     {  
  49.                             mid2 = (left2 + right2) / 2;  
  50.                             midmid2 = (mid2 + right2) / 2;  
  51.                         ans2 = dis((n - m / tan(midmid1)), m, (n - m / tan(midmid1) - m / tan(mid2)), 2*m);  
  52.                             ans3 = dis((n - m / tan(midmid1)), m, (n - m / tan(midmid1) - m / tan(midmid2)), 2*m);  
  53.                             ans44 = ans11 / c + ans2 / b + dis(0, 3 * m, (n - m / tan(midmid1) - m / tan(mid2)), 2*m) / a;  
  54.                             ans55 = ans11 / c + ans3 / b + dis(0, 3 * m, (n - m / tan(midmid1) - m / tan(midmid2)),2*m) /a;  
  55.                             if(ans44 > ans55) left2 = mid2;  
  56.                             else right2 = midmid2;  
  57.             }  
  58.             if(ans4 > ans44) left1 = mid1;  
  59.             else right1 = midmid1;  
  60.         }  
  61.         printf("%.10lf\n", ans4);  
  62.     }  
  63.   
  64. return 0;  
  65.   
  66. }  

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值