问题描述
作为水污染管理部门的一名雇员,你需要监控那些被有意无意倒入河流、湖泊和海洋的污染物。你的其中一项工作就是估计污染物对不同的水生态系统(珊瑚礁、产卵地等等)造成的影响。
你计算所使用的模型已经在图1中被说明。海岸线(图1中的水平直线)为x轴,污染源位于原点(0, 0)。污染的蔓延呈半圆形,多边形代表了被波及的生态系统。你需要计算出生态系统被污染的面积,也就是图中深蓝色部分。
输入格式
输入文件包含仅包含一组测试数据。
每组测试数据第一行为两个整数n (3 <= n <= 100), r (1 <= r <= 1000),n表示了多边形的顶点个数,r表示了污染区域的半径;
接下来n行,每行包含两个整数xi (-1500 <= xi <= 1500), yi (0 <= yi <=1500),表示每个顶点的坐标,以逆时针顺序给出;
数据保证多边形不自交或触及自身,没有顶点会位于圆弧上。
输出格式
输出多边形被圆心位于原点、半径为r的半圆覆盖的面积。
答案的绝对误差不得超过10^-3。
样例输入
6 10
-8 2
8 2
8 14
0 14
0 6
-8 14
样例输出
101.576437872
数据规模和约定
存在约30%的数据,n = 3,r <= 20;
存在另外约30%的数据,n <= 10,r <= 100,坐标范围不超过100;
存在另外约10%的数据,n <= 100,r <= 150,坐标范围不超过250;
存在另外约30%的数据,n <= 100,r <= 1000,数据存在梯度;
对于100%的数据,满足题目所示数据范围。
由于之前做出来,没来的急整理,现在整理一下发出来供大家参考。
个人思路:利用凸包多变型面积计算公式
1、逆时针选取多边形的边。2、如果一条边的一个顶点在圆外一个顶点在圆内,则需要求出交点。
3、一条边对多边形面积的贡献用差乘来计算,对这道题来说不是整个边对多边形都有贡献。(原点为o)
1)整个边ab都在圆内:S=(向量oa x 向量ob )/2
2)顶点a在圆外,顶点b在圆内,求出与圆的交点为mid: s=(向量oa x 向量o mid )/2 +扇型面积(mid,b,r);
3)顶点b在圆外,顶点a在圆内,求出与圆的交点为mid: s=扇型面积(a,mid,r)+(向量omid x 向量o b)/2 ;
(有顺序的)
4)将所有的边的贡献都加起来就为多边形与圆相交的面积了。
原理图:
凸包面积:以p为起点,多变形的顶点为终点的有向向量,逆时针计算有向面积,作差乘即可。
源码:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
new Main().run();
}
/**
* @author ZQ
*向量
*/
class vector{
double x,y;
public vector(double x,double y) {
this.x=x;this.y=y;
}
public double getLength(){
return Math.sqrt(x*x+y*y);
}
public boolean isInCircular(double r){
return ((x*x+y*y)<=r*r)?true:false;
}
public vector decrease(vector o){
return new vector(x-o.x,y-o.y);
}
/**向量的叉乘 s=1/2*axb=1/2absin(deg);
* @param o
* @return
*/
public double xmultiply(vector o){
return x*o.y-y*o.x;
}
public vector add(vector o){
return new vector(x+o.x,y+o.y);
}
}
public double getSectorialA(vector a,vector b,double r){
double deg=a.xmultiply(b)/(a.getLength()*b.getLength());
if(deg<-1)deg=-1;
if(deg>1)deg=1;
deg=Math.asin(deg);
return r*r*deg/2;
}
public double handle(vector a,vector b,double r){
boolean flaga=a.isInCircular(r);
boolean flagb=b.isInCircular(r);
double result=0;
if(flaga&&flagb){//这条边全部在圆内
result=a.xmultiply(b)/2;
}else if(flaga^flagb){//一端在里边 一端在外边
//找到这条线段与弧的交点 二分找
vector p=a,m=b,mid = null;
for(int i=0;i<40;i++){
mid=new vector((p.x+m.x)/2, (p.y+m.y)/2);
if(mid.isInCircular(r)==flaga){
p=mid;
}else{
m=mid;
}
}
if(flaga){
result=a.xmultiply(mid)/2+getSectorialA(mid, b, r);
}else{
result=getSectorialA(a, mid, r)+mid.xmultiply(b)/2;
}
}else{//两端点都在外边 1 、线段穿过在圆内,2、线段不穿过圆内
vector p=a,m=b,mid = null,newmid=null;
//找距离原点最近的点
for(int i=0;i<40;i++){
mid=new vector((p.x+m.x)/2, (p.y+m.y)/2);
newmid=mid.add(new vector((m.x-p.x)*0.0001, (m.y-p.y)*0.0001));
if(mid.getLength() vs=new ArrayList();
for(int i=1;i<=n;i++){
x=sc.nextDouble();
y=sc.nextDouble();
vector v=new vector(x, y);
vs.add(v);
}
for(int i=0;i