这恶心的题,细节实在是有点多啊= =….
BZOJ1038传送门
听说洛谷上的数据好像要苛刻一些,所以也放一个门
洛谷P2600传送门
H村村长dadzhi决定在村中建立一个瞭望塔,以此加强村中的治安。将H村抽象为平面轮廓折线,用(x1, y1), (x2, y2), …. (xn, yn)来描述H村的形状,这里x1 < x2 < …< xn。瞭望塔可以建造在[x1, xn]间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。在不同的位置建造瞭望塔,所需要建造的高度是不同的。请你写一个程序,帮助dadzhi村长计算塔的最小高度。
输入格式:
第一行包含一个整数n,表示轮廓折线的节点数目。接下来第一行n个整数, 为x1 ~ xn. 第三行n个整数,为y1 ~ yn。
输出格式:
包含一个实数,为塔的最小高度,精确到小数点后三位。
多画几个图大概就能看出来做法
正解是半平面交。把相邻村庄之间的连线,只有在连线的一侧可以同时看见两个村庄,很明显是个半平面交。关于半平面交部分,可以参考题目:BZOJ1007水平可见直线 (题目传送门)
很明显瞭望塔要么在转折点处,要么在某个村庄的正上方,建在中间一定不如建在某个端点优(画几条直线模拟一下就知道了)。
因此,处理出合法的区域后,枚举每个转折点和村庄,计算建立瞭望塔的高度更新答案。
实现细节比较多
比如答案为-0.000时注意要输出0
还有边界问题,要么在边界加框,要么就要手动算边界点,坐标尽量开得大一点。实现时me写的是后者。
不要eps也可以过,不用太在意eps的问题
#include
#include
#include
#include
using namespace std ;
const double eps = 1e-9 ;
int N ;
struct Vector{
double x , y ;
double len(){ return sqrt( x * x + y * y ) ; }
Vector(){} ;
Vector( double x_ , double y_ ) :
x(x_) , y(y_){} ;
};
typedef Vector Point ;
typedef Vector Vv ;
int dcmp( const double &x ){
//if( x > -eps && x < eps ) return 0 ;
//return ( x > eps ? 1 : -1 ) ;
if( x == 0 ) return 0 ;
return x > 0 ? 1 : -1 ;
}
Vv operator + ( const Vv &A , const Vv &B ){ return Vv( A.x + B.x , A.y + B.y ) ; }
Vv operator - ( const Vv &A , const Vv &B ){ return Vv( A.x - B.x , A.y - B.y ) ; }
Vv operator * ( const Vv &A , const double &p ){ return Vv( A.x * p , A.y * p ) ; }
Vv operator / ( const Vv &A , const double &p ){ return Vv( A.x / p , A.y / p ) ; }
bool operator ==( const Vv &A , const Vv &B ){ return A.x == B.x && A.y == B.y ; }
bool operator < ( const Vv &A , const Vv &B ){ return A.x < B.x || ( A.x == B.x && A.y < B.y ) ; }
double Dot ( const Vv &A , const Vv &B ){ return A.x * B.x + A.y * B.y ; }
double Cross( const Vv &A , const Vv &B ){ return A.x * B.y - A.y * B.x ; }
struct Line{
Point pp ;
Vector vv ;
double ang ;
Line(){} ;
Line( Point pp_ , Vector vv_ ):
pp(pp_) , vv(vv_){ang = atan2( vv.y , vv.x ) ;} ;
bool operator < ( const Line &A ) const {
return ang < A.ang ;
}
};
int fr , ba ;
Point a[305] , vtx[305] ;
Line La[305] , pne[305] ;
Point glt( Point P , Vector u , Point Q , Vector v ){
double t1 = Cross( Q - P , v ) / Cross( u , v ) ;
return P + u * t1 ;
}
void halfPlaneIntersect( Line *La , int siz , Point *p , Line *pne , int &fr , int &ba ){
sort( La + 1 , La + siz + 1 ) ;
fr = 1 , ba = 0 ;
pne[++ba] = La[1] ;
for( int i = 2 ; i <= siz ; i ++ ){
while( ba > fr && dcmp( Cross( p[ba-1] - La[i].pp , La[i].vv ) ) >= 0 ) ba -- ;
while( ba > fr && dcmp( Cross( p[fr] - La[i].pp , La[i].vv ) ) >= 0 ) fr ++ ;
++ ba ;
pne[ba] = La[i] ;
if( dcmp( pne[ba].ang - pne[ba-1].ang ) == 0 ){
ba -- ;
if( dcmp( Cross( pne[ba].pp - La[i].pp , La[i].vv ) ) >= 0 ) pne[ba] = La[i] ;
}
if( ba > fr )
p[ba-1] = glt( pne[ba].pp , pne[ba].vv , pne[ba-1].pp , pne[ba-1].vv ) ;
}
}
void solve(){
for( int i = 1 ; i < N ; i ++ )
La[i] = Line( a[i] , a[i+1] - a[i] ) ;
halfPlaneIntersect( La , N - 1 , vtx , pne , fr , ba ) ;
double ans = 1e12 ;
sort( vtx + fr , vtx + ba + 1 - 1 ) ;
a[0] = Point( -1e15 , 0 ) ;
a[N+1] = Point( 1e15 , 0 ) ;
for( int i = 0 , now = fr ; i <= N && now <= ba - 1 ; i ++ ){
while( now <= ba - 1 && a[i].x <= vtx[now].x && a[i+1].x >= vtx[now].x ){
double len = ( vtx[now].x - a[i].x ) / ( a[i+1].x - a[i].x ) ;
ans = min( ans , vtx[now].y - ( a[i].y + ( ( a[i+1] - a[i] ) * len ).y ) ) ;
now ++ ;
}
}
vtx[fr-1] = Point( -1e10 , pne[fr].pp.y + ( pne[fr].vv.y / pne[fr].vv.x ) * (-1e10 - pne[fr].pp.x ) ) ;
vtx[ba] = Point( 1e10 , pne[ba-1].pp.y + ( pne[ba].vv.y / pne[ba].vv.x ) * ( 1e10 - pne[ba].pp.x ) ) ;
for( int i = fr - 1 , now = 1 ; i <= ba - 1 && now <= N ; i ++ ){
while( now <= N && vtx[i].x <= a[now].x && vtx[i+1].x >= a[now].x ){
double len = ( a[now].x - vtx[i].x ) / ( vtx[i+1].x - vtx[i].x ) ;
ans = min( ans , ( vtx[i].y + ( ( vtx[i+1] - vtx[i] ) * len ).y ) - a[now].y ) ;
now ++ ;
}
}
if( dcmp( ans ) <= 0 ) ans = 0 ;
printf( "%.3f" , ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ )
scanf( "%lf" , &a[i].x ) ;
for( int i = 1 ; i <= N ; i ++ )
scanf( "%lf" , &a[i].y ) ;
solve() ;
}