您的位置:首页 > 其它

算法训练 Pollution Solution

2018-03-11 15:09 155 查看
问题描述  作为水污染管理部门的一名雇员,你需要监控那些被有意无意倒入河流、湖泊和海洋的污染物。你的其中一项工作就是估计污染物对不同的水生态系统(珊瑚礁、产卵地等等)造成的影响。



  你计算所使用的模型已经在图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()<newmid.getLength()){
m=mid;
}else{
p=mid;
}
}
if(mid.isInCircular(r)){//在圆内
result=handle(a, mid, r)+handle(mid, b, r);
}else{
result=getSectorialA(a, b, r);
}
}
return result;
}
public  void run(){
Scanner sc=new Scanner(System.in);
int n = sc.nextInt();
int r = sc.nextInt();
double x,y;
double result=0;
List<vector> vs=new ArrayList<vector>();
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<n;i++){
result+=handle(vs.get(i), vs.get(((i+1)==n)?0:i+1), r);
}
System.out.println(result);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: