之前沒學過三分,不過猜想過它的那個原理,跟二分的差不多,都是逼近的思想 這次月賽的D題,是三分的,比賽時,搞不出,回來才發現枚舉AB的時間可能會逼近不到端點的情況,加了這個判斷就過了。。。 回來再試了下兩重三分的。。。貼貼代碼。。。
 /**//*
題意:有兩條線段,AB,CD 在AB上走速度為P,CD上走為Q,其余地方為R
問從A到D的最短時間

三分,可以處理凸函數峰值,及單調函數
m1,m2取法只要在left,right之內就行吧,這里用黃金比例
記住:
如果m1更接近峰值,則去掉m2右邊
如果m2更接近峰值,則去掉m1左邊
*/
#include<cstdio>
#include<cstring>
#include<cmath>

const double eps=1e-5;

struct Point
  {
double x,y;
}A,B,C,D,AA,DD;

double P,Q,R;

double dist(Point &a,Point &b)
  {
return sqrt(eps+(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); //要加eps 比較那些加不加應該沒關系
}
double f(double tc)
  {
DD.x=D.x+(C.x-D.x)/dist(C,D)*tc*Q;
DD.y=D.y+(C.y-D.y)/dist(C,D)*tc*Q;
return tc+dist(AA,DD)/R;
}
int main()
  {
//freopen("in","r",stdin);
int T;
scanf("%d",&T);
 while(T--) {
scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y);
scanf("%lf%lf%lf%lf",&C.x,&C.y,&D.x,&D.y);
scanf("%lf%lf%lf",&P,&Q,&R);
double ans=1000000000.0;
bool flag=true;
for(double ta=0;flag;ta+=0.01)//枚舉在AB走了多長時間
 {
 if(ta>eps+dist(A,B)/P) {//ta>dist(A,B)/P,這樣會漏掉了端點,但有可能取到端點,所以要特判!
ta=dist(A,B)/P;
flag=false;
}
AA.x=A.x+(B.x-A.x)/dist(A,B)*ta*P;
AA.y=A.y+(B.y-A.y)/dist(A,B)*ta*P;
double left=0,right=dist(C,D)/Q;
while(right-left>eps)
 {
double m1=right-(right-left)*0.618;
double m2=left+(right-left)*0.618;
if(f(m1)>f(m2)+eps)left=m1;
else right=m2;
}
if(f(left)+ta+eps<ans)ans=f(left)+ta;
}
printf("%.2f\n",ans);
}
return 0;
}
上面那個是枚舉在AB走的時間,比較慢,這個兩重三分的,0ms
 /**//*
由于有兩個自變量ta,tc分別表示在AB,CD上走的時間,需要降維
上面那種是枚舉降維,這里對ta也三分來降維,快了好多
*/
#include<cstdio>
#include<cstring>
#include<cmath>

const double eps=1e-5;

struct Point
  {
double x,y;
}A,B,C,D,AA,DD;

double P,Q,R;

double dist(Point &a,Point &b)
  {
return sqrt(eps+(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); //要加eps 比較那些加不加應該沒關系
}
double f(double tc)
  {
DD.x=D.x+(C.x-D.x)/dist(C,D)*tc*Q;
DD.y=D.y+(C.y-D.y)/dist(C,D)*tc*Q;
return tc+dist(AA,DD)/R;
}
double cal(double ta)
  {
AA.x=A.x+(B.x-A.x)/dist(A,B)*ta*P;
AA.y=A.y+(B.y-A.y)/dist(A,B)*ta*P;
double left=0,right=dist(C,D)/Q;
while(right-left>eps)
 {
double m1=right-(right-left)*0.618;
double m2=left+(right-left)*0.618;
if(f(m1)>f(m2)+eps)left=m1;
else right=m2;
}
return f(left)+ta;
}
int main()
  {
//freopen("in","r",stdin);
int T;
scanf("%d",&T);
 while(T--) {
scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y);
scanf("%lf%lf%lf%lf",&C.x,&C.y,&D.x,&D.y);
scanf("%lf%lf%lf",&P,&Q,&R);
double low=0,high=dist(A,B)/P;//注意high是這個,不是無窮,因為限定了在AB內
while(high-low>eps)
 {
double m=high-(high-low)*0.618;
double mm=low+(high-low)*0.618;
if(cal(m)>cal(mm)+eps)low=m;
else high=mm;
}
printf("%.2f\n",cal(low));
}
return 0;
}
|