算法训练 Pollution Solution

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


  你计算所使用的模型已经在图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);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值