半平面交 模板 poj 3335 poj 3130 poj 1474 判断半平面交是否为空集

半平面交模板


const double pi= acos(-1.0);
#define arc(x) (x / 180 * pi)
const double EPS = 1e-8;
const int Max_N = 105;
struct Point{
double x,y;
Point(){}
Point(double x, double y):x(x),y(y){}
Point operator - (Point p){
return Point(x- p.x , y - p.y ) ;
}
Point operator + (Point p){
return Point(x+ p.x , y + p.y ) ;
}
double operator ^ ( Point p){
return x*p.y - y * p.x;
}
double operator * (Point p){ //内积 点乘
return x*p.x + y * p.y ;
}
Point operator * (double d){
return Point( x*d, y*d ) ;
}
//点位置相同
bool operator == (Point p){
return fabs(x - p.x ) < EPS && fabs( y - p.y )< EPS ;
}
};
// 两点表示的向量
struct pVector{
Point s,e; // s 起点, e终点
double a,b,c; // 一般式 ax + by + c = 0
pVector(){}
pVector(Point s, Point e):s(s),e(e){}
void output(){
cout<<s.x <<" "<<s.y << ":" <<e.x <<" "<< e.y <<endl;
}
// 向量与点的叉乘[ 点相对向量位置判断 ]
//返回值> 0 , 则 点在向量的右侧, <0 则 点在向量的左侧
bool operator^( Point p ){
return ( p - s) ^ (e- s);
}
bool onRight(Point p){ // 点在直线向量的右边
return ( (p - s)^( e - s) )> EPS ;
}
// 向量与向量(_off)的 叉乘
double operator ^ (pVector _off){
return ((e - s )^( _off.e - _off.s )) ;
}
//从两点表示 转换为 一般表示
bool pton(){
a = s.y - e.y ;
b = e.x - s.x ;
c = s.x * e.y - s.y * e.x ;
return true;
}
//两向量 (直线) 平行
bool parallel(pVector _off){
return fabs( (e - s)^( _off.e - _off.s ) ) < EPS ;
}
//两直线 交点, 参数 目标直线[_off]
Point crossLPt(pVector _off){
// 注意先判断平行和重合
double d1 = ( (_off.e - _off.s)^( _off.s - s ) ) ;
double d2 = ( ( _off.e - _off.s )^( e - s ) ) ;
return s + ( e - s )*( d1 / d2 ) ;
}
};
//------------半平面交 -------------//
//半平面 计算 极角函数
double hpc_pa(pVector _off){
return atan2( _off.e.y - _off.s.y , _off.e.x - _off.s.x ) ;
}
// 半平面交 排序函数 [优先顺序:1极角 2. 前面的直线在后面的左边 ]
bool hpc_cmp(pVector l , pVector r){
double lp = hpc_pa(l) , rp = hpc_pa(r);
if( fabs(lp - rp) > EPS )
return lp < rp ;
return ( (l.s - r.s)^(r.e - r.s) ) < -EPS ;
}
//用于 计算的 双端队列
pVector dequeue[Max_N];
Point pt[Max_N] ; // 返回半平面交的 交点
int n;
//获取 半平面交的 多边形(多边形的核) o(n log(2n))
//参数: 向量集合[l], 向量数量[ln] (半平面方向在向量的左边)
//函数运行后 如果n[即返回多边形的点数量] 为 0 则不存在半平面交的多边形(不存在区域或区域面积无穷大)
int halfPanelCross(pVector _off[] , int ln){
int i, tn ;
sort( _off , _off + ln , hpc_cmp ) ;
// 平面在向量左边 的筛选
for( i = tn = 1 ; i < ln ; i++ ){ // 处理极角相同的,选择向量方向最左边的
if( fabs( hpc_pa( _off[i] ) - hpc_pa( _off[i-1] ) ) > EPS ) //除去极角相同的向量
_off[tn ++] = _off[i] ;
}
ln = tn ; // 预处理后长度
int bot = 0, top = 1 ; // 队列的两个指针首尾
dequeue[0] = _off[0] ;
dequeue[1] = _off[1] ;
for(i = 2 ; i < ln ; i++){ // 枚举有向直线
if( dequeue[top].parallel( dequeue[top - 1] ) ||
dequeue[bot].parallel( dequeue[bot + 1] ) ) // 剪枝 : 如果f方向相反的2个向量平行 则 为空集
return 0;
while( bot < top && _off[i].onRight( dequeue[top].crossLPt(dequeue[top - 1]) ) ) // 交点在向量的右边,删除头无用边 top--
top-- ;
while( bot< top && _off[i].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ) ) //交点在向量的右边,删除尾无用边 bot ++
bot ++ ;
dequeue[ ++ top] = _off[i] ; // 将当前直线加入队列
}
// 处理链接处 若队首交点在队尾直线的右边, 队首出列 , 若队尾的交点在队首直线的右边, 队尾出列
while( bot < top && dequeue[bot].onRight( dequeue[top].crossLPt(dequeue[top - 1]) ))
top -- ;
while(bot < top && dequeue[top].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ))
bot ++ ;
if( top <= bot + 1) return 0; // 若队列为空, 则空集
n = 0;
//计算交点(注意不同直线形成的交点可能重合)
for(i = bot ; i < top ; i++){
pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]) ;
}
if(bot < top + 1)
pt[n ++] = dequeue[bot].crossLPt( dequeue[top] ) ;
return n;
}

分析:  如果多边形顶点是按照 顺时针给出, 那么向量的构造为:


for(i = 0; i<m ; i++){
List[i] = pVector( p[(i+1) % m ], p[i] ) ; //构造有向直线(半平面在向量的左边)
}

如果多变形顶点是按照 逆时针 给出, 那么向量的构造为:


for(i = 0; i<m ; i++){
List[i] = pVector( p[i] , p[(i+1) % m ] ) ; //构造有向直线(半平面在向量的左边)
}

题目来源:

http://poj.org/problem?id=3335

判断是否存在半平面交

代码如下:


const double pi= acos(-1.0);
#define arc(x) (x / 180 * pi)
const double EPS = 1e-8;
const int Max_N = 105;
struct Point{
double x,y;
Point(){}
Point(double x, double y):x(x),y(y){}
Point operator - (Point p){
return Point(x- p.x , y - p.y ) ;
}
Point operator + (Point p){
return Point(x+ p.x , y + p.y ) ;
}
double operator ^ ( Point p){
return x*p.y - y * p.x;
}
double operator * (Point p){ //内积 点乘
return x*p.x + y * p.y ;
}
Point operator * (double d){
return Point( x*d, y*d ) ;
}
//点位置相同
bool operator == (Point p){
return fabs(x - p.x ) < EPS && fabs( y - p.y )< EPS ;
}
};
// 两点表示的向量
struct pVector{
Point s,e; // s 起点, e终点
double a,b,c; // 一般式 ax + by + c = 0
pVector(){}
pVector(Point s, Point e):s(s),e(e){}
void output(){
cout<<s.x <<" "<<s.y << ":" <<e.x <<" "<< e.y <<endl;
}
// 向量与点的叉乘[ 点相对向量位置判断 ]
//返回值> 0 , 则 点在向量的右侧, <0 则 点在向量的左侧
bool operator^( Point p ){
return ( p - s) ^ (e- s);
}
bool onRight(Point p){ // 点在直线向量的右边
return ( (p - s)^( e - s) )> EPS ;
}
// 向量与向量(_off)的 叉乘
double operator ^ (pVector _off){
return ((e - s )^( _off.e - _off.s )) ;
}
//从两点表示 转换为 一般表示
bool pton(){
a = s.y - e.y ;
b = e.x - s.x ;
c = s.x * e.y - s.y * e.x ;
return true;
}
//两向量 (直线) 平行
bool parallel(pVector _off){
return fabs( (e - s)^( _off.e - _off.s ) ) < EPS ;
}
//两直线 交点, 参数 目标直线[_off]
Point crossLPt(pVector _off){
// 注意先判断平行和重合
double d1 = ( (_off.e - _off.s)^( _off.s - s ) ) ;
double d2 = ( ( _off.e - _off.s )^( e - s ) ) ;
return s + ( e - s )*( d1 / d2 ) ;
}
};
//------------半平面交 -------------//
//半平面 计算 极角函数
double hpc_pa(pVector _off){
return atan2( _off.e.y - _off.s.y , _off.e.x - _off.s.x ) ;
}
// 半平面交 排序函数 [优先顺序:1极角 2. 前面的直线在后面的左边 ]
bool hpc_cmp(pVector l , pVector r){
double lp = hpc_pa(l) , rp = hpc_pa(r);
if( fabs(lp - rp) > EPS )
return lp < rp ;
return ( (l.s - r.s)^(r.e - r.s) ) < -EPS ;
}
//用于 计算的 双端队列
pVector dequeue[Max_N];
Point pt[Max_N] ; // 返回半平面交的 交点
int n;
//获取 半平面交的 多边形(多边形的核) o(n log(2n))
//参数: 向量集合[l], 向量数量[ln] (半平面方向在向量的左边)
//函数运行后 如果n[即返回多边形的点数量] 为 0 则不存在半平面交的多边形(不存在区域或区域面积无穷大)
int halfPanelCross(pVector _off[] , int ln){
int i, tn ;
sort( _off , _off + ln , hpc_cmp ) ;
// 平面在向量左边 的筛选
for( i = tn = 1 ; i < ln ; i++ ){ // 处理极角相同的,选择向量方向最左边的
if( fabs( hpc_pa( _off[i] ) - hpc_pa( _off[i-1] ) ) > EPS ) //除去极角相同的向量
_off[tn ++] = _off[i] ;
}
ln = tn ; // 预处理后长度
int bot = 0, top = 1 ; // 队列的两个指针首尾
dequeue[0] = _off[0] ;
dequeue[1] = _off[1] ;
for(i = 2 ; i < ln ; i++){ // 枚举有向直线
if( dequeue[top].parallel( dequeue[top - 1] ) ||
dequeue[bot].parallel( dequeue[bot + 1] ) ) // 剪枝 : 如果f方向相反的2个向量平行 则 为空集
return 0;
while( bot < top && _off[i].onRight( dequeue[top].crossLPt(dequeue[top - 1]) ) ) // 交点在向量的右边,删除头无用边 top--
top-- ;
while( bot< top && _off[i].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ) ) //交点在向量的右边,删除尾无用边 bot ++
bot ++ ;
dequeue[ ++ top] = _off[i] ; // 将当前直线加入队列
}
// 处理链接处 若队首交点在队尾直线的右边, 队首出列 , 若队尾的交点在队首直线的右边, 队尾出列
while( bot < top && dequeue[bot].onRight( dequeue[top].crossLPt(dequeue[top - 1]) ))
top -- ;
while(bot < top && dequeue[top].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ))
bot ++ ;
if( top <= bot + 1) return 0; // 若队列为空, 则空集
n = 0;
//计算交点(注意不同直线形成的交点可能重合)
for(i = bot ; i < top ; i++){
pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]) ;
}
if(bot < top + 1)
pt[n ++] = dequeue[bot].crossLPt( dequeue[top] ) ;
return n;
}
pVector List[Max_N];
Point p[Max_N];
int main(){
int t, i , j;
int m;
scanf("%d" , &t);
while(t--){
scanf("%d",&m) ;
for(i = 0 ; i< m ; i++)
scanf("%lf%lf", &p[i].x , &p[i].y) ;
for(i = 0; i<m ; i++){
List[i] = pVector( p[(i+1) % m ], p[i] ) ; //构造有向直线(半平面在向量的左边)
}
if ( halfPanelCross(List, m) )
puts("YES");
else puts("NO") ;
}
return 0;
}

poj 3130 判段半平面交是否为空集,  多边形的顶点是按逆时针输入。 只需更改下
构造有向直线处的代码。

poj 1474  判断 半平面交 是否为空集, 多边形的顶点是按 顺时针输入, 只需更改下 输出代码,
即可。

半平面交 模板 poj 3335 poj 3130 poj 1474
判断半平面交是否为空集,码迷,mamicode.com

半平面交 模板 poj 3335 poj 3130 poj 1474
判断半平面交是否为空集

时间: 2024-12-20 09:46:58

半平面交 模板 poj 3335 poj 3130 poj 1474 判断半平面交是否为空集的相关文章

POJ 3525 /// 半平面交 模板

题目大意: 给定n,接下来n行逆时针给定小岛的n个顶点 输出岛内离海最远的点与海的距离 半平面交模板题 将整个小岛视为由许多半平面围成 那么以相同的比例缩小这些半平面 一直到缩小到一个点时 那个点就是离海最远的点 #include <cstdio> #include <cmath> #include <vector> #include <algorithm> using namespace std; const double eps=1e-10; doubl

poj 3335 /poj 3130/ poj 1474 半平面交 判断核是否存在 / poj1279 半平面交 求核的面积

1 /*************** 2 poj 3335 点序顺时针 3 ***************/ 4 #include <iostream> 5 #include <cmath> 6 #include <algorithm> 7 using namespace std; 8 const double eps = 1e-8; 9 const double maxn = 0x7f7f7f7f; 10 int dcmp(double x){ 11 if(fabs(

poj 3335 Rotating Scoreboard(半平面交)

Rotating Scoreboard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 6420   Accepted: 2550 Description This year, ACM/ICPC World finals will be held in a hall in form of a simple polygon. The coaches and spectators are seated along the ed

POJ 3335 半平面交求多边形的核

Rotating Scoreboard Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4899   Accepted: 1946 Description This year, ACM/ICPC World finals will be held in a hall in form of a simple polygon. The coaches and spectators are seated along the ed

POJ 3335

/*半平面交求核心的增量法: 假设前N-1个半平面交,对于第N个半平面,只需用它来交前N-1个平面交出的多边形. 算法开始时,调整点的方向为顺时针方向,对于是否为顺时针,只需求出其面积,若为正,必为逆时针的. 对于每相邻两点求出一条直线,用该直线去交其半平面,并求出交点及判断原多边形点的方位. */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #i

POJ 3675 Telescope 简单多边形和圆的面积交

这道题得控制好精度,不然会贡献WA  QAQ 还是那个规则: int sgn(double x){ if(x > eps) return 1; else if(x < - eps) return -1; else return 0; } 思路:把简单多边形的每一个点和原点连线,就把这个多边形和圆的交变成了多个三角形与圆的交,根据有向面积的思路,加加减减就可以得到公共面积. 贴上代码了- #include <cstdio> #include <cstring> #incl

POJ 3177 Redundant Paths POJ 3352 Road Construction(双连通)

POJ 3177 Redundant Paths POJ 3352 Road Construction 题目链接 题意:两题一样的,一份代码能交,给定一个连通无向图,问加几条边能使得图变成一个双连通图 思路:先求双连通,缩点后,计算入度为1的个数,然后(个数 + 1) / 2 就是答案(这题由于是只有一个连通块所以可以这么搞,如果有多个,就不能这样搞了) 代码: #include <cstdio> #include <cstring> #include <algorithm&

poj 1556 zoj1721 BellmanFord 最短路+判断直线相交

http://poj.org/problem?id=1556 The Doors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 6120   Accepted: 2455 Description You are to find the length of the shortest path through a chamber containing obstructing walls. The chamber will a

poj 1679 The Unique MST (判断最小生成树是否唯一)

The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20679   Accepted: 7255 Description Given a connected undirected graph, tell if its minimum spanning tree is unique. Definition 1 (Spanning Tree): Consider a connected, undire