HDU3867 (计算几何+极角扫描线)
Light and Shadow
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 666 Accepted Submission(s): 185
Problem Description
A nuclear explosion happened and how many sticks on the ground will be illuminated by the radiation? You can assume that the ground is flat, and no radiation can go through the sticks, and any two sticks have no intersections.
To simplify the problem, no two endpoints located on the path of a single radiation.
Input
For each case, the first line contains the number of sticks n(1~10,000), until the end of file.
The second line includes (sx, sy) to denote the position of explosion center.
Then following n lines are the sticks expressed in (ax, ay) and (bx, by).
The second line includes (sx, sy) to denote the position of explosion center.
Then following n lines are the sticks expressed in (ax, ay) and (bx, by).
Output
For each case, print the number of sticks which would be illuminated.
Sample Input
6 0.5 0.5 1.0 -1.0 1.0 1.0 1.5 1.0 1.5 -1.0 2.0 -1.0 2.0 1.0 -1.0 -1.0 -1.0 1.0 -1.5 1.0 -1.5 -1.0 -2.0 -1.0 -2.0 1.0
Sample Output
2
Source
链接:http://acm.hdu.edu.cn/showproblem.php?pid=3867
题意:原子弹爆炸,一些互不相交的线段,求能辐射到的线段 (可以将原子弹爆炸点视为泛光源) 以辐射源为中心对周围的点按照极坐标角度进行排序,然后在极坐标上使用扫描线方法。
维护一个集合,集合内的元素是与扫描线相交的线段,排序依据是线段与扫描线的交点到辐射源的距离。该集合中的最小元素就是被照射到的线段。
在扫描线运动前后,如果有两个线段存在于容器中,这两个线段与扫描线的交点到辐射源的距离远近关系不会发生变化。若发生变化,表示扫描线运动范围内两个线段有交点,与题目提供的已知条件不符。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <cmath>
#include <map>
using namespace std;
const int N = 10005;
const double INF = 1E200;
const double EP = 1E-8;
const double PI = acos(-1.0);
int sgn(double x)
{
if(fabs(x) < EP)return 0;
if(x < 0)return -1;
else return 1;
}
struct POINT
{
double x;
double y;
POINT(double a=0, double b=0)
{
x=a; //constructor
y=b;
}
POINT operator -(const POINT &b)const
{
return POINT(x - b.x,y - b.y);
}
bool operator < (const POINT &b) const //顺时针
{
return x * b.y < y * b.x;
}
//叉积
double operator ^(const POINT &b)const
{
return x*b.y - y*b.x;
}
double diso()
{
return sqrt(x * x + y * y);
}
} p,st,ed,now;
bool equal_point(POINT p1,POINT p2) // 判断两个点是否重合
{
return ( (abs(p1.x-p2.x)<EP)&&(abs(p1.y-p2.y)<EP) );
}
struct LINESEG
{
POINT s;
POINT e;
LINESEG(POINT a, POINT b)
{
s=a;e=b;
}
LINESEG() { }
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为2是相交
//只有第一个值为2时,交点才有意义
//pair<int,POINT> ans = line1 & line2;
pair<int,POINT> operator &(const LINESEG &b)const
{
POINT res = s;
if(sgn((s-e)^(b.s-b.e)) == 0)
{
if(sgn((s-b.e)^(b.s-b.e)) == 0)
return make_pair(0,res);//重合
else return make_pair(1,res);//平行
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
} line[N*2];
struct Line
{
int id;
int type; //扫入扫出线
double ang; //极角
POINT s,e;
Line() { }
Line( int id, int type, double ang ): id(id), type(type), ang(ang) { }
bool operator < (const Line &b) const//对set进行重载
{
if(equal_point(b.s,s) && equal_point(b.e,e))return false;
pair<int,POINT> ans1 = LINESEG(POINT(0,0),now) & LINESEG(s,e);
pair<int,POINT> ans2 = LINESEG(POINT(0,0),now) & LINESEG(b.s,b.e);
POINT co1 = ans1.second;
POINT co2 = ans2.second;
return co1.diso()<co2.diso();
}
} linesao[N*2];
bool cmp( const Line& lhs, const Line& rhs )//对线进行极角排序,小到大,逆时针
{
int tmp = sgn( lhs.ang - rhs.ang );
if ( tmp ) return tmp < 0;
else return lhs.type > rhs.type;
}
double angg[N];
/*bool cmp(const Line &a, const Line &b) //极角排序 从-PI到-PI内
{
return atan2(a.s->y, a.s->x) < atan2(b.s->y, b.s->x);
}*/
void input(int i,int n)//其实可以不记录ang,直接用上面的极角排序函数就行
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
st = POINT(x1,y1)-p,ed = POINT(x2,y2)-p;
line[i] = LINESEG(st,ed);
if(ed<st) swap(st,ed);
double ang1 = atan2(st.y,st.x);
double ang2 = atan2(ed.y,ed.x);
angg[i] = ang2;
linesao[i*2] = Line(i,-1,ang1);
linesao[i*2].s = POINT(st.x,st.y);
linesao[i*2].e = POINT(ed.x,ed.y);
linesao[i*2+1] = Line(i,1,ang2);
linesao[i*2+1].s = POINT(ed.x,ed.y);
linesao[i*2+1].e = POINT(st.x,st.y);
}
bool sgcross_with_ax(Line a) //与射线相交判断 good 以前不知道的东西
{
POINT tmp(-1.0, 0.0);
return (a.s ^ a.e) * (a.s ^ tmp) > 0.0
&& (a.s ^ tmp) * (tmp ^ a.e) > 0.0;//这里我用了>=错了
}
set<Line> sset;
bool vis[N];
void init()
{
memset(vis,false,sizeof(vis));
sset.clear();
}
int main()
{
int n;
while(~scanf("%d",&n))
{
init();//别忘了加!!!
scanf("%lf%lf",&p.x,&p.y);
for(int i=0; i<n; i++)
{
input(i,n);
}
sort(linesao,linesao+n*2,cmp);
for(int i = 0; i<n*2; i++)//先把起始位置相交的插入到set
{
now = linesao[i].s;
if(linesao[i].type == 1 && sgcross_with_ax(linesao[i]))
sset.insert(linesao[i]);
}
for(int i=0; i<n*2; i++)
{
now = linesao[i].s;
if(linesao[i].type==1)
sset.insert(linesao[i]);
else //set必须全部赋值才能erase
{
Line k = Line(linesao[i].id,1,angg[linesao[i].id]);
k.s = linesao[i].e; k.e = linesao[i].s;
sset.erase(k);
}
if(!sset.empty())
vis[sset.begin()->id] = true;
}
int ans = 0;
for(int i = 0; i<n; i++)
if(vis[i])ans++;
printf("%d\n", ans);
}
return 0;
}
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <cmath>
#include <map>
using namespace std;
const int N = 10005;
const double INF = 1E200;
const double EP = 1E-8;
const double PI = acos(-1.0);
int sgn(double x)
{
if(fabs(x) < EP)return 0;
if(x < 0)return -1;
else return 1;
}
struct POINT
{
double x;
double y;
POINT(double a=0, double b=0)
{
x=a; //constructor
y=b;
}
POINT operator -(const POINT &b)const
{
return POINT(x - b.x,y - b.y);
}
bool operator < (const POINT &b) const //顺时针
{
return x * b.y < y * b.x;
}
//叉积
double operator ^(const POINT &b)const
{
return x*b.y - y*b.x;
}
double diso()
{
return sqrt(x * x + y * y);
}
} p,st,ed,now;
bool equal_point(POINT p1,POINT p2) // 判断两个点是否重合
{
return ( (abs(p1.x-p2.x)<EP)&&(abs(p1.y-p2.y)<EP) );
}
struct LINESEG
{
POINT s;
POINT e;
LINESEG(POINT a, POINT b)
{
s=a;e=b;
}
LINESEG() { }
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为2是相交
//只有第一个值为2时,交点才有意义
//pair<int,POINT> ans = line1 & line2;
pair<int,POINT> operator &(const LINESEG &b)const
{
POINT res = s;
if(sgn((s-e)^(b.s-b.e)) == 0)
{
if(sgn((s-b.e)^(b.s-b.e)) == 0)
return make_pair(0,res);//重合
else return make_pair(1,res);//平行
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
} line[N*2];
struct Line
{
int id;
int type; //扫入扫出线
double ang; //极角
POINT s,e;
Line() { }
Line( int id, int type, double ang ): id(id), type(type), ang(ang) { }
bool operator < (const Line &b) const//对set进行重载
{
if(equal_point(b.s,s) && equal_point(b.e,e))return false;
pair<int,POINT> ans1 = LINESEG(POINT(0,0),now) & LINESEG(s,e);
pair<int,POINT> ans2 = LINESEG(POINT(0,0),now) & LINESEG(b.s,b.e);
POINT co1 = ans1.second;
POINT co2 = ans2.second;
return co1.diso()<co2.diso();
}
} linesao[N*2];
bool cmp( const Line& lhs, const Line& rhs )//对线进行极角排序,小到大,逆时针
{
int tmp = sgn( lhs.ang - rhs.ang );
if ( tmp ) return tmp < 0;
else return lhs.type > rhs.type;
}
double angg[N];
/*bool cmp(const Line &a, const Line &b) //极角排序 从-PI到-PI内
{
return atan2(a.s->y, a.s->x) < atan2(b.s->y, b.s->x);
}*/
void input(int i,int n)//其实可以不记录ang,直接用上面的极角排序函数就行
{
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
st = POINT(x1,y1)-p,ed = POINT(x2,y2)-p;
line[i] = LINESEG(st,ed);
if(ed<st) swap(st,ed);
double ang1 = atan2(st.y,st.x);
double ang2 = atan2(ed.y,ed.x);
angg[i] = ang2;
linesao[i*2] = Line(i,-1,ang1);
linesao[i*2].s = POINT(st.x,st.y);
linesao[i*2].e = POINT(ed.x,ed.y);
linesao[i*2+1] = Line(i,1,ang2);
linesao[i*2+1].s = POINT(ed.x,ed.y);
linesao[i*2+1].e = POINT(st.x,st.y);
}
bool sgcross_with_ax(Line a) //与射线相交判断 good 以前不知道的东西
{
POINT tmp(-1.0, 0.0);
return (a.s ^ a.e) * (a.s ^ tmp) > 0.0
&& (a.s ^ tmp) * (tmp ^ a.e) > 0.0;//这里我用了>=错了
}
set<Line> sset;
bool vis[N];
void init()
{
memset(vis,false,sizeof(vis));
sset.clear();
}
int main()
{
int n;
while(~scanf("%d",&n))
{
init();//别忘了加!!!
scanf("%lf%lf",&p.x,&p.y);
for(int i=0; i<n; i++)
{
input(i,n);
}
sort(linesao,linesao+n*2,cmp);
for(int i = 0; i<n*2; i++)//先把起始位置相交的插入到set
{
now = linesao[i].s;
if(linesao[i].type == 1 && sgcross_with_ax(linesao[i]))
sset.insert(linesao[i]);
}
for(int i=0; i<n*2; i++)
{
now = linesao[i].s;
if(linesao[i].type==1)
sset.insert(linesao[i]);
else //set必须全部赋值才能erase
{
Line k = Line(linesao[i].id,1,angg[linesao[i].id]);
k.s = linesao[i].e; k.e = linesao[i].s;
sset.erase(k);
}
if(!sset.empty())
vis[sset.begin()->id] = true;
}
int ans = 0;
for(int i = 0; i<n; i++)
if(vis[i])ans++;
printf("%d\n", ans);
}
return 0;
}