您的位置:首页 > 其它

和菜鸟一起学算法之三分法求极值问题

2012-07-01 13:15 260 查看
午后的阳光,那么灿烂,如果不是温度过高,那么去西湖看看风景还是不错的。想着,现在西湖边应该是平静的湖面,加上无数知了在柳枝上演奏着交响曲吧。小看了下非诚勿扰,那男生为了女孩唐静付出了7年,唉,可是他错了,女孩根本不爱他,不过期间他的执着和付出,很让我感动,也许自己不太像他那样,才会让自己有现在的处境吧。也许吧。小感慨下。不过现在也挺好的,上上班,写写文章,然后天气凉快点还可以到处玩,杭州是个旅游休闲的好地方啊。扯远了,吃了饭回来,还是有些小感触啊。

回归正题,既然二分法讲完了,那么继续三分法吧。二分法适用于求单调的时候用的,就比如说排序好的数组,那是递增的或者递减的。如果像出现了下图二次函数那样的怎么求他的最值呢?



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

zoj3203LightBulb

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

TimeLimit:1SecondMemoryLimit:32768KB

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

Comparedtowildleopard'swealthiness,hisbrothermildleopardisratherpoor.Hishouseisnarrowandhehasonlyonelightbulbinhishouse.Everynight,heiswanderinginhisincommodioushouse,
thinkingofhowtoearnmoremoney.Oneday,hefoundthatthelengthofhisshadowwaschangingfromtimetotimewhilewalkingbetweenthelightbulbandthewallofhishouse.Asuddenthoughtranthroughhismindandhewantedtoknowthemaximumlength
ofhisshadow.



Input

ThefirstlineoftheinputcontainsanintegerT(T<=100),indicatingthenumberofcases.

EachtestcasecontainsthreerealnumbersH,handDinoneline.Histheheightofthelightbulbwhilehistheheightofmildleopard.Disdistancebetweenthelightbulbandthewall.Allnumbers
areinrangefrom10-2to103,bothinclusive,andH-h>=10-2.

Output

Foreachtestcase,outputthemaximumlengthofmildleopard'sshadowinoneline,accurateuptothreedecimalplaces..

SampleInput

3

210.5

20.53

434

SampleOutput

1.000

0.750

4.000

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

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

#include<stdio.h> intmain() { intt; scanf("%d",&t); while(t--) { doubleh,H,D; scanf("%lf%lf%lf",&H,&h,&D); doubleleft=0,right=D*h/H; intsize=100; doublemid,midmid,ans1,ans2; while(size--) { mid=(left+right)/2; midmid=(mid+right)/2; ans1=((h*D-H*mid)/(H-h)*H)/((h*D-H*mid)/(H-h)+D)+mid; ans2=((h*D-H*midmid)/(H-h)*H)/((h*D-H*midmid)/(H-h)+D)+midmid; if(ans1>ans2)right=midmid; elseleft=mid; } printf("%.3lf\n",ans1); } return0; }

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

再来看一道题目

hdu2438Turnthecorner

TimeLimit:3000/1000MS(Java/Others)MemoryLimit:32768/32768K(Java/Others)
TotalSubmission(s):404AcceptedSubmission(s):103


ProblemDescription

Mr.Westboughtanewcar!Soheistravellingaroundthecity.Onedayhecomestoaverticalcorner.Thestreetheiscurrentlyinhasawidthx,thestreethewantstoturntohasawidthy.Thecarhasalengthlandawidthd.CanMr.Westgoacrossthecorner?


Input

Everylinehasfourrealnumbers,x,y,landw.Proceedtotheendoffile.


Output

Ifhecangoacrossthecorner,print"yes".Print"no"otherwise.


SampleInput

10613.54

10614.54



SampleOutput

yes

no




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

#include<cstdio>
#include<cmath>
constdoublepi=3.1415926535;
intmain()
{
doublex,y,l,w;
while(scanf("%lf%lf%lf%lf",&x,&y,&l,&w)!=EOF)
{
intsize=100;
doubleleft=0,right=pi/2;
doublemid=0,midmid=0,ans1,ans2;
if(x<w||y<w){printf("no\n");continue;}
while(size--)
{
mid=(left+right)/2;
midmid=(mid+right)/2;
ans1=(x-l*sin(mid)-w/cos(mid))/tan(mid);
ans2=(x-l*sin(midmid)-w/cos(midmid))/tan(midmid);
if(ans1<ans2)right=midmid;
elseleft=mid;
}
if(fabs(ans1)>y)printf("no\n");
elseprintf("yes\n");
}
return0;
}

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

xmu1125.越野车大赛

Description

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

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



Input

  输入数据的第一行为两个正整数NM(N<=3000,M<=1000),表示一块路面的长和宽。

  第二行为三个正整数S1,S2,S3(0<S1,S2,S3<=100),从上至下依次表示各个路面上越野车的最高速度。

Output

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

SampleInput

3010

253

SampleOutput

13.7427361525

Hint

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

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

#include<iostream>
#include<cstdio>
#include<cmath>
usingnamespacestd;
constdoublepi=3.141592654;

inlinedoubledis(doublex1,doubley1,doublex2,doubley2)
{
returnsqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

intmain()
{
doublen,m,a,b,c;
while(cin>>n>>m)
{
cin>>a>>b>>c;
doubleleft1=0,right1=pi/2;
doublemid1,midmid1,mid2,midmid2,ans1,ans11,ans2,ans3,ans4,ans5,ans44,ans55;
intsize=30;
while(size--)
{
mid1=(left1+right1)/2;
midmid1=(mid1+right1)/2;
ans1=dis((n-m/tan(mid1)),m,n,0);
ans11=dis((n-m/tan(midmid1)),m,n,0);
intsize1=30;
doubleleft2=0,right2=pi/2;
while(size1--)
{
mid2=(left2+right2)/2;
midmid2=(mid2+right2)/2;
ans2=dis((n-m/tan(mid1)),m,(n-m/tan(mid1)-m/tan(mid2)),2*m);
ans3=dis((n-m/tan(mid1)),m,(n-m/tan(mid1)-m/tan(midmid2)),2*m);
ans4=ans1/c+ans2/b+dis(0,3*m,(n-m/tan(mid1)-m/tan(mid2)),2*m)/a;
ans5=ans1/c+ans3/b+dis(0,3*m,(n-m/tan(mid1)-m/tan(midmid2)),2*m)/a;
if(ans4>ans5)left2=mid2;
elseright2=midmid2;
}

left2=0,right2=pi/2;
size1=30;
while(size1--)
{
mid2=(left2+right2)/2;
midmid2=(mid2+right2)/2;
ans2=dis((n-m/tan(midmid1)),m,(n-m/tan(midmid1)-m/tan(mid2)),2*m);
ans3=dis((n-m/tan(midmid1)),m,(n-m/tan(midmid1)-m/tan(midmid2)),2*m);
ans44=ans11/c+ans2/b+dis(0,3*m,(n-m/tan(midmid1)-m/tan(mid2)),2*m)/a;
ans55=ans11/c+ans3/b+dis(0,3*m,(n-m/tan(midmid1)-m/tan(midmid2)),2*m)/a;
if(ans44>ans55)left2=mid2;
elseright2=midmid2;
}
if(ans4>ans44)left1=mid1;
elseright1=midmid1;
}
printf("%.10lf\n",ans4);
}

return0;

}



至此,三分法就是这样的了,很简单吧,总结下,就是要求出那个公式,然后调用那个框架。只要理解了原理,题目都是换汤不换药的。小憩片刻,还是回公司看看书去吧。这时代,不看书,不多学点东西,会被时代淘汰的。。。。。。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: