 /**//*
題意:一個人在起點(x0,y0)處開始向下滑雪,給出n段門(一段一段向下),人必須穿過每一段,
不能越過去,問最后到達終點的最小滑行距離

有個結論:最優的滑行路線是由一些線段組成的。而且這些線段的端點必定是起點或者門的兩個端點,
并有可能有一條垂直于X軸的線段(最后一段,從某個點直接滑到最后一個門)。

然后對每一段的兩個端點,求其到終點的最小距離dp[i][side]
轉移是枚舉下一個直達的端點(是直達,不用繞彎)
dp[i,side] = min( dp[i,side] , dp[j,s] + dist() )
往下枚舉時更新左端點,右端點!!

啟示:題目規模知道應該是O(n^2)算法,最優性猜想dp
直線走最近,枚舉可以直達的端點!!
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const double EPS = 1e-10;
const double DINF = 1e300;
const int MAXN = 1010;

struct Point
  {
double x,y;
 Point() {}
Point(double _x,double _y)
 {
x = _x;
y = _y;
}
};

Point seg[MAXN][2];
double dp[MAXN][2];

int sign(double x)
  {
return x<-EPS?-1:x>EPS;
}

Point intersert(Point &A, Point &B, double y)
  {
double dx = A.x-B.x;
double dy = A.y-B.y;
return Point(A.x+(y-A.y)*dx/dy, y);
}
double dist(Point &A,Point &B)
  {
double x = A.x-B.x;
double y =A.y-B.y;
return sqrt(x*x+y*y);
}

bool leftTurn(Point &O,Point &A,Point &B)//B is in the left of OA
  {
double x1 = (A.x-O.x), y1 = (A.y-O.y);
double x2 = (B.x-O.x), y2 = (B.y-O.y);
return sign( x1*y2 - x2*y1 ) > 0 ;
}

bool noRightTurn(Point &O,Point &A,Point &B)//B is in the left of ,or on OA
  {
double x1 = (A.x-O.x), y1 = (A.y-O.y);
double x2 = (B.x-O.x), y2 = (B.y-O.y);
return sign( x1*y2 - x2*y1 ) >= 0 ;
}

int main()
  {
#ifdef ONLINE_JUDGE
#else
freopen("in","r",stdin);
#endif

int n;
while(scanf("%d",&n),n)
 {
scanf("%lf%lf",&seg[0][0].x,&seg[0][0].y);
for(int i=1;i<=n;i++)
 {
scanf("%lf%lf%lf",&seg[i][0].y,&seg[i][0].x,&seg[i][1].x);
seg[i][1].y = seg[i][0].y;
}
dp[n][0] = dp[n][1] = 0.0;
for( int i = n-1 ,j; i >= 0 ; i-- )
for( int side = 0 ; side < 2 ; side++ )
 {
dp[i][side] = DINF;
 Point next[2] = { seg[i+1][0] , seg[i+1][1] };
Point pt = seg[i][side];
for( j = i+1 ; j<=n ; j++ )
 {
//不能直達時要跳出
if( leftTurn( pt, seg[j][1], next[0]) || leftTurn( pt, next[1], seg[j][0]))break;
//往下枚舉時要更新左端點和右端點!!
if( noRightTurn( pt, next[0], seg[j][0]))
 {
next[0] = seg[j][0];
dp[i][side] = min(dp[i][side],dist(pt,next[0])+dp[j][0]);
}
if( noRightTurn( pt, seg[j][1], next[1]))
 {
next[1] = seg[j][1];
dp[i][side] = min(dp[i][side],dist(pt,next[1])+dp[j][1]);
}
}
if( j > n )//directly to finish line
 {
double y = seg[n][0].y;
next[0] = intersert(pt,next[0],y);
next[1] = intersert(pt,next[1],y);
//由于直達終點線,可以不用到端點處
if(pt.x < next[0].x )dp[i][side] = min(dp[i][side], dist(pt,next[0]));
else if(pt.x > next[1].x)dp[i][side] = min(dp[i][side], dist(pt,next[1]));
else dp[i][side] = min(dp[i][side], pt.y-y);//直下
}
if( i == 0 )break;
}
printf("%.10f\n",dp[0][0]);
}
return 0;
}

|
|
常用鏈接
隨筆分類
Links
搜索
最新評論

|
|