三分

三分搜索法

  二分法作为分治中最常见的方法,适用于单调函数,逼近求解某点的值。但当函数是凸性函数时,二分法就无法适用,这时三分法就可以大显身手~~


       如图,类似二分的定义LeftRightmid = (Left + Right) / 2midmid = (mid + Right) / 2; 如果mid靠近极值点,则Right = midmid;否则(midmid靠近极值点),则Left = mid;

程序模版如下:

double Calc(Type a)
{
    /*
根据题目的意思计算 */
}

void Solve(void)
{
    double Left, Right;
    double mid, midmid;
    double mid_value, midmid_value;
    Left = MIN; Right = MAX;
    while (Left + EPS < Right)
    {
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);
        //
假设求解最大极值.
        if (mid_area >= midmid_area) Right = midmid;
        else Left = mid;
    }
}

现根据几道的OJ题目来分析三分法的具体实现。

buaa 1033 Easy Problem
http://acm.buaa.edu.cn/oj/problem_show.php?c=0&p=1033

题意为在一条线段上找到一点,与给定的P点距离最小。很明显的凸性函数,用三分法来解决。
Calc
函数即为求某点到P点的距离。

ZOJ 3203 Light Bulb
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3203


如图,人左右走动,求影子L的最长长度。
根据图,很容易发现当灯,人的头部和墙角成一条直线时(假设此时人站在A),此时的长度是影子全在地上的最长长度。当人再向右走时,影子开始投影到墙上,当人贴着墙,影子长度即为人的高度。所以当人从A点走到墙,函数是先递增再递减,为凸性函数,所以我们可以用三分法来求解。

下面只给出Calc函数,其他直接套模版即可。
double Calc(double x)
{
    return (h * D - H * x) / (D - x) + x;
}

heru 5081 Turn the corner 08年哈尔滨regional网络赛
http://acm.hrbeu.edu.cn/index.php?act=problem&id=1280


汽车拐弯问题,给定X, Y, l, d判断是否能够拐弯。首先当X或者Y小于d,那么一定不能。
其次我们发现随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解。

这里的Calc函数需要比较繁琐的推倒公式:
s = l * cos(θ) + w * sin(θ) - x;
h = s * tan(θ) + w * cos(θ);
其中s为汽车最右边的点离拐角的水平距离, h为里拐点最高的距离, θ范围从090

POJ 3301 Texas Trip
http://acm.pku.edu.cn/JudgeOnline/problem?id=3301

题意为给定n(n <= 30)个点,求出饱含这些点的面积最小的正方形。

有两种解法,一种为逼近法,就是每次m分角度,求出最符合的角度,再继续m分,如此进行times次,即可求出较为精确的解。(m 大概取10, times30即可)

第二种解法即为三分法,首先旋转的角度只要在0180度即可,超过180度跟前面的相同的。坐标轴旋转后,坐标变换为:
X’ = x * cosa - y * sina;
y’ = y * cosa + x * sina;

至于这题的函数是否是凸性的,为什么是凸性的,我也无法给出准确的证明,希望哪位路过的大牛指点一下~~

例题更新(2010.5.5)
hdu 3400 Line belt

http://acm.hdu.edu.cn/showproblem.php?pid=3400
典型的三分法,先三分第一条线段,找到一个点,然后根据这个点再三分第二条线段即可,想出三分的思路基本就可以过了。

对于求解一些实际问题,当公式难以推导出来时,二分、三分法可以较为精确地求解出一些临界值,且效率也是令人满意的。
(转自http://hi.baidu.com/czyuan_acm/blog/item/8cc45b1f30cefefde1fe0b7e.html

posted @ 2011-08-25 21:27 NewPandaKing 阅读(57) 评论(0) 编辑

hdu Turn the corner

Turn the corner

Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 151 Accepted Submission(s): 61

 

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 a length 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判断是否能够拐弯。首先当X或者Y小于d,那么一定不能。
其次我们发现随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解。

这里的Calc函数需要比较繁琐的推倒公式:
s = l * cos(θ) + w * sin(θ) - x;
h = s * tan(θ) + w * cos(θ);
其中s为汽车最右边的点离拐角的水平距离, h为里拐点最高的距离, θ范围从090

3分搜索法

#include <iostream>
#include <stdio.h>
#include <math.h>
usingnamespace std;
double pi = acos(-1.0);
double x,y,l,w,s,h;
double cal(double a)
{
   
s = l*cos(a)+w*sin(a)-x;
    h = s*tan(a)+w*cos(a);
    return h;
}
int main()
{
   
double left,right,mid,midmid;
   
while(scanf("%lf%lf%lf%lf",&x,&y,&l,&w)!=EOF)
 
   {
        left = 0.0;
       
right = pi/2;
       
while(fabs(right-left)>1e-8)
       
{
            mid = (left+right)/2;
           
midmid = (mid+right)/2;
           
if(cal(mid)>=cal(midmid))right = midmid;
           
else left = mid;
       
}
        if(cal(mid)<=y)printf("yes\n");
       
else printf("no\n");
   
}
    return0;
}

posted @ 2011-08-25 21:22 NewPandaKing 阅读(39) 评论(0) 编辑

hdu 2298 Toxophily

Toxophily

Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 99 Accepted Submission(s): 56

 

Problem Description

The recreation center of WHU ACM Team has indoor billiards, Ping Pang, chess and bridge, toxophily, deluxe ballrooms KTV rooms, fishing, climbing, and so on.
We all like toxophily.

Bob is hooked on toxophily recently. Assume that Bob is at point (0,0) and he wants to shoot the fruits on a nearby tree. He can adjust the angle to fix the trajectory. Unfortunately, he always fails at that. Can you help him?

Now given the object's coordinates, please calculate the angle between the arrow and x-axis at Bob's point. Assume that g=9.8N/m.

 

Input

The input consists of several test cases. The first line of input consists of an integer T, indicating the number of test cases. Each test case is on a separated line, and it consists three floating point numbers: x, y, v. x and y indicate the coordinate of the fruit. v is the arrow's exit speed.
Technical Specification

1. T ≤ 100.
2. 0 ≤ x, y, v ≤ 10000.

 

Output

For each test case, output the smallest answer rounded to six fractional digits on a separated line.
Output "-1", if there's no possible answer.

Please use radian as unit.

 

Sample Input

3

0.222018 23.901887 121.909183

39.096669 110.210922 20.270030

138.355025 2028.716904 25.079551

 

Sample Output

1.561582

-1

-1

分析:

 网上这道题的解法几乎都是2分法效率还可以,但是用数学方法解题可以实现0ms通过。

 用公式,根据正交分解坐标系,得出方程的通式。

x^2*g/(2*v^2)*tan^2(ß) - x*tan(ß) +y + x^2*g/(2*v^2) = 0;

 即:a = g*pow(x,2)/(2*pow(v,2));

    b = -x;

    c = y + g*pow(x,2)/(2*pow(v,2));

根据求根公式求出根。

注意讨论:

1 x==0&&y==0时,ß = 0

2 x==0&&y>0时,ß=90

3方程无解时 ß=-1

4方程的解为负数时,ß=-1;(0<=ß<=90)。

 

#include <iostream>
#include <stdio.h>
#include <math.h>
usingnamespace std;
int main()
{
   
int t;
   
double a,b,c,angle,z;
   
double x,y,v,g = 9.8,T,ans1,ans2;
   
scanf("%d",&t);
   
while(t--)
   
{
        scanf("%lf%lf%lf",&x,&y,&v);
  
     if(x==0&&y==0)
       
printf("0\n");
       
elseif(x==0&&y>0)
       
printf("90\n");
       
else
       
{
            a = g*pow(x,2)/(2*pow(v,2));
           
b = -x;
            c = y+a;
            T = pow(b,2) - 4*a*c;
           
angle = 0;
           
if(T<0)
           
printf("-1\n");
           
else
           
{
                ans1 = ((-b)+pow(T,1.0/2))/(2*a);
               
ans2 = ((-b)-pow(T,1.0/2))/(2*a);
               
if(ans1>=0) angle = atan(ans1);
               
if(ans2>=0)
 
               {
                    z =  atan(ans2);
                    if(z<angle) angle = z;
                   
printf("%.6f\n",angle);
               
}
                if(ans1<0&&ans2<0)
                   
printf("-1\n");
           
}
        }
    }
    return0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值