/**//* 题意:一个人在起点(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
搜索
最新评论
|
|