您的位置:首页 > 理论基础 > 计算机网络

15北京网络赛 E题

2015-09-25 12:06 501 查看

题目描述:

题目5 : Border Length

时间限制:1000ms

单点时限:1000ms

内存限制:256MB

描述

Garlic-Counting Chicken is a special species living around the Lake of Peking University.

A Garlic-Counting Chicken always flies out from home in the morning, goes to some fixed points looking for buuugs(a kind of worm), and comes back home in the afternoon.

Students from the School of Life Sciences find that, a Garlic-Counting Chicken always flies a straight line between two points, and its trace never goes across itself. The students want to find out the relationship between Garlic-Counting Chicken and the Lake. So they ask you to calculate the length of border of the common area between the Lake and one Garlic-Counting Chicken’s daily trace.

输入

There are no more than 10 test cases.

For each test case, the first line contains n, the number of points on the trace in order. 1<=n<=1000.

Then n lines follow. Each line is a pair of integer x, y representing the n points’ coordinates in order.

The last line contains three integers x,y and r descripting the Lake, The Lake is a circle. Its center is at (x,y) and its radius is r.

The input ends if n = 0.

All |x|, |y|, |r| are smaller than 107.

输出

For each test case, output the length of the border of the common area. Please round the answer to the closest integer.

样例输入

4

-10 -10

-10 10

10 10

10 -10

-10 -10 20

4

-10 -10

-10 10

10 10

10 -10

-10 -10 4

4

-10 -10

-10 10

10 10

10 -10

0 0 10

4

-40 -40

-40 40

40 40

40 -40

0 0 50

0

样例输出

71

14

63

297

题解:

首先思路很好确立:看多边形的每一条边,搞出来和圆的交点,交点搞到圆上,线段在圆里面的会计算到答案上面.之后就是圆弧对答案的贡献了,圆弧上面所有的焦点之间的那一段,找出里面的中点,看这个中点是在多边形的里面还是外面.里面的话把圆心角也算上去就好.

重点:

计算几何=成熟的板子+成熟的代码能力+自信

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const double eps = 1e-8;
const double PI = acos(-1.0);
const int maxn = 4000+100;

int dcmp(double x)
{
if(fabs(x) < eps)
return 0;
if(x>0)
return 1;
return -1;
}

struct Point
{
double x,y;
Point() {}
Point(double _x,double _y)
{
x = _x;
y = _y;
}
Point operator -(const Point &b)const
{
return Point(x - b.x,y - b.y);
}
//叉积
double operator ^(const Point &b)const
{
return x*b.y - y*b.x;
}
int operator ==(const Point &b)const
{
if(dcmp(x-b.x)==0&&dcmp(y-b.y)==0)
return 1;
return 0;
}
//点积
double operator *(const Point &b)const
{
return x*b.x + y*b.y;
}
double mo()
{
double ans = 0;
ans = sqrt(x*x+y*y);
return ans;
}
void change(double r)
{
double k = r/mo();
x *= k;
y *= k;
}
};

struct Line
{
Point s,e;
Line() {}
Line(Point _s,Point _e)
{
s = _s;
e = _e;
}
};

bool OnSeg(Point P,Line L)
{
return
dcmp((L.s-P)^(L.e-P)) == 0 &&
dcmp((P.x - L.s.x) * (P.x - L.e.x)) <= 0 &&
dcmp((P.y - L.s.y) * (P.y - L.e.y)) <= 0;
}

bool inter(Line l1,Line l2)
{
return
max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
dcmp((l2.s-l1.e)^(l1.s-l1.e))*dcmp((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
dcmp((l1.s-l2.e)^(l2.s-l2.e))*dcmp((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}

//*判断点在任意多边形内
//射线法,poly[]的顶点数要大于等于3,点的编号0~n-1
//返回值
//-1:点在凸多边形外
//0:点在凸多边形边界上
//1:点在凸多边形内
int inPoly(Point p,Point poly[],int n)
{
int cnt;
Line ray,side;
cnt = 0;
ray.s = p;
ray.e.y = p.y;
ray.e.x = -100000000000.0;//-INF,注意取值防止越界
for(int i = 0; i < n; i++)
{
side.s = poly[i];
side.e = poly[(i+1)%n];
if(OnSeg(p,side))return 0;
//如果平行轴则不考虑
if(dcmp(side.s.y - side.e.y) == 0)
continue;
if(OnSeg(side.s,ray))
{
if(dcmp(side.s.y - side.e.y) > 0)cnt++;
}
else if(OnSeg(side.e,ray))
{
if(dcmp(side.e.y - side.s.y) > 0)cnt++;
}
else if(inter(ray,side))
cnt++;
}
if(cnt % 2 == 1)return 1;
else return -1;
}
/*
线段和圆心在(0,0)处的圆的相交情况
解二次函数的方法,对直线没有限制,用的是参数方程.
num =
0 没有交点
1 只有一个交点
2 有两个交点,并且res[0]是靠近a的那个交点
*/
void circle_cross_line(Point a, Point b, double r, int &num, Point *res)
{
double xa = a.x, ya = a.y, xb = b.x, yb = b.y;
double dx = xb-xa, dy = yb-ya;
double A = dx*dx+dy*dy, B = 2*dx*xa+2*dy*ya, C = xa*xa+ya*ya-r*r;
double delta = B*B - 4*A*C;
if(dcmp(delta)<0)
{
num = 0;
return;
}
if(dcmp(delta)==0)
{
double t1 = (-B)/(2*A);
if(dcmp(t1)>=0&&dcmp(t1-1)<=0)
{
num = 1;
res[0] = Point(xa+t1*dx, ya+t1*dy);
return;
}
else
{
num=0;
return;
}
}
double t1 = (-B-sqrt(delta))/(2*A), t2 = (-B+sqrt(delta))/(2*A);
num = 0;
if(dcmp(t1)>=0&&dcmp(t1-1)<=0)
{
res[num] = Point(xa+t1*dx, ya+t1*dy);
num++;
}
if(dcmp(t2)>=0&&dcmp(t2-1)<=0)
{
res[num] = Point(xa+t2*dx, ya+t2*dy);
num++;
}
}
/*
点和圆的相对关系
-1  在圆内部
0 在圆上
1 在圆的外面
*/
int relation(Point b, int r)
{
double dst=b.mo();
if (dcmp(dst-r)<0)return -1;
if (dcmp(dst-r)==0)return 0;
return 1;
}
double dist(Point a, Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

Point jiao[maxn], key;
int jiaon;
Point p[maxn];
Point tmp[10];
int tmpnum;
int n;
double x, y, r;
/*
从a到b的对应的圆弧
*/
double area_sector(Point a, Point b)
{
double si = a^b;//叉积来判断和180度的关系.
double co = a*b;
co = co/(a.mo())/b.mo();
co = min(1.0, co);
co = max(-1.0, co);
co = acos(co);
if(dcmp(si)==0)
{
if(a==b)
return 0;
else
return PI;
}
if(dcmp(si) > 0)
{
return co;
}
else
{
return 2*PI - co;
}
}
/*
圆心在原点的圆上面的点搞出来圆心角
*/
double getAngle(Point a)
{
double co = a.x/a.mo(), si = a.y/a.mo();
co = min(1.0, co);
co = max(-1.0, co);
if(dcmp(si)>=0)
{
return acos(co);
}
else
{
return 2*PI - acos(co);
}

}
/*
按照圆心角来排序
*/
bool cmp3(Point a, Point b)
{
double a_a = getAngle(a), a_b = getAngle(b);
return dcmp(a_a-a_b)<=0;
}
/*
找到两个点中间的那个点
注意的是一般b都大于a,但是最后一次b会小于a
*/
Point getMidPoint(Point a, Point b)
{
double a_a = getAngle(a), a_b = getAngle(b);
if(a_a > a_b)
a_b += 2*PI;
double c_a = (a_a+a_b)/2;
Point c = Point(cos(c_a)*r, sin(c_a)*r);
return c;
}

void solve()
{
for(int i = 0; i<n; i++)
{
p[i].x -= x;
p[i].y -= y;
}
x = 0;
y = 0;
p
= p[0];
double ans = 0;
jiaon = 0;
for(int i = 0; i<n; i++)
{
int j = i+1;
int stai = relation(p[i], r), staj = relation(p[j], r);
int nowi = i, nowj = j;
if(stai*staj == -1)
{
if(stai==1)
swap(nowi, nowj);
circle_cross_line(p[nowi], p[nowj], r, tmpnum, tmp);
ans += dist(p[nowi], tmp[0]);
jiao[jiaon++] = Point(tmp[0].x, tmp[0].y);
}
else if(stai==0 && staj==0)
{
ans += dist(p[nowi], p[nowj]);
jiao[jiaon++] = Point(p[nowi].x, p[nowi].y);
jiao[jiaon++] = Point(p[nowj].x, p[nowj].y);
}
else if(stai*staj==0)
{
if(stai != 0)
{
swap(nowi, nowj);
swap(stai, staj);
}
if(staj==1)
jiao[jiaon++] = Point(p[nowi].x, p[nowi].y);
else
{
jiao[jiaon++] = Point(p[nowi].x, p[nowi].y);
ans += dist(p[nowi], p[nowj]);
}
}
else if(stai==1&&staj==1)
{
circle_cross_line(p[nowi], p[nowj], r, tmpnum, tmp);
if(tmpnum == 2)
{
ans += dist(tmp[0], tmp[1]);
jiao[jiaon++] = Point(tmp[0].x, tmp[0].y);
jiao[jiaon++] = Point(tmp[1].x, tmp[1].y);
}
}
else
{
ans += dist(p[nowi], p[nowj]);
}
}
sort(jiao, jiao+jiaon, cmp3);
jiao[jiaon] = jiao[0];
double ansjiao = 0;
if(jiaon >= 2)
{
for(int i = 0; i<=jiaon - 1; i++)
{
Point zhong = getMidPoint(jiao[i], jiao[i+1]);
//printf("----- %lf  %lf\n", zhong.x, zhong.y);
if(inPoly(zhong, p, n)==1)
{
ansjiao += area_sector(jiao[i], jiao[i+1]);
}
}
}
else
{
if(relation(p[0], r) >= 0 && relation(p[1],r) >= 0)
ansjiao = 2*PI;
else
ansjiao = 0;
}
ansjiao *= r;
ans += ansjiao;
ll ttans = (ans+0.5+eps);
printf("%lld\n", ttans);
}

int main()
{
freopen("5Ein.txt", "r", stdin);
//freopen("5Eout.txt", "w", stdout);
while(scanf("%d", &n))
{
if(n==0)
break;
for(int i = 0; i<n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y);
scanf("%lf%lf%lf", &x, &y, &r);
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: