UVA 11978 / Lightoj 1358 圆与简单多边形交的面积

像UVA 11177的话是一个圆与多边形相交的特殊情况, 圆心在原点, 原点一定在凸多边形内部,可以用特殊的做法做

而这道题的话就需要这类问题的通用做法

在计算多边形面积的时候,我们任意找一个点,与多边形上任意一条边形成一个三角形,计算N个三角形的有向面积和

在圆与多边形交的情况下,这个点就可以是圆心,求的是N个三角形与圆相交的有向面积。

三角形与圆相交可以分为四种情况。具体可见代码,要注意的地方就是第三种情况的判定条件

/*
1. 平行向量叉积为0的性质 可以用来判断点是否在直线上
2. 点积的符号 可以用来判断点与线段的位置关系  角度等等
3. 叉积的大小可以用于计算距离等等

*/



#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <stack>
#include <cctype>
#include <cmath>
#include <vector>
#include <sstream>
#include <bitset>
#include <deque>
#include <iomanip>
using namespace std;
#define pr(x) cout << #x << " = " << x << endl;
#define bug cout << "bugbug" << endl;
#define ppr(x, y) printf("(%d, %d)\n", x, y);
#define pdr(x, y) printf("(%.3f, %.3f)\n", x, y);
#define pfun(a, b) printf("x = %d   f(x) = %d\n", a, b);

typedef long long ll;
typedef pair<int, int> P;
const int MOD = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e2 + 4;
const int maxm = 1e4 + 4;
inline double add(double a, double b = 0){
	return fabs(a + b) < eps * (fabs(a) + fabs(b)) ? 0 : a + b;
}
inline int dcmp(double d){
	if (fabs(d) < eps) return 0;
	return d > 0 ? 1 : -1;
}
struct Point;
typedef Point Vector;
typedef vector<Point> Polygon;
struct Point{
	double x, y;
	Point(double _x = 0, double _y = 0):x(_x), y(_y){
	}

	//Point相减显然可以得到Vector
	Vector operator + (const Vector &rhs) const {	return Vector(add(x, rhs.x), add(y, rhs.y)); }
	Vector operator - (const Vector &rhs) const {	return Vector(add(x, -rhs.x), add(y, -rhs.y)); }
	Vector operator * (const double &rhs) const {	return Vector(rhs * x, rhs * y);}


	void operator += (const Vector &rhs) {	*this = *this + rhs; }
	void operator -= (const Vector &rhs) {	*this = *this - rhs; }
	void operator *= (const double &rhs) {	*this = *this * rhs; }


	double dot (const Vector &rhs) const {	return add(x * rhs.x, y * rhs.y);}
	double det (const Vector &rhs) const {	return add(x * rhs.y, -y * rhs.x);}

	bool operator < (const Point& rhs) const{
		if (!dcmp(x-rhs.x)) return dcmp(y-rhs.y) == -1;
		return dcmp(x-rhs.x) == -1;

	}

	bool operator == (const Point& rhs) const{	return dcmp(x-rhs.x) == 0 && dcmp(y-rhs.y) == 0; }


	double length() {	return sqrt(dot(*this));}

	//逆时针旋转
	Vector rotate(double rad) {	return Vector(add(x * cos(rad), -y * sin(rad)), add(x * sin(rad), y * cos(rad))); }
	void print() {	pdr(x, y); }
	void read(){
		scanf("%lf%lf", &x, &y);
	}
};
const Point O = Point{0, 0};

Vector MakeVector(double angle, double len){
	// MakeVector(angle, len) 的反向向量是  MakeVector(angle+pi, len) 或者 MakeVector(angle, -len) 
	return Point{add(cos(angle) * len), add(sin(angle) * len)};
}
bool on_seg(Point s, Point t, Point p){
	return dcmp((s-p).det(t-p)) == 0 && dcmp((s-p).dot(t-p)) <= 0;
	//此处有没有等于 就是含不含端点
}

//求直线的交点
Point intersection(Point p1, Point p2, Point q1, Point q2){//p1p2 与  q1q2不能是平行的
	return p1 + (p2 - p1) * ((q2 - q1).det(q1 - p1) / (q2 - q1).det(p2 - p1));
}
double dis_point_line(Point P, Point A, Point B){
//	注意参数的顺序, 利用的是叉积
	return fabs((A-P).det(B-P) / (A-B).length());
}

double dis_point_seg(Point P, Point A, Point B){
	Vector v1 = P - A, v2 = B - A, v3 = P - B;
	if (dcmp(v1.dot(v2)) == -1) return v1.length();
	else if (dcmp(v3.dot(v2)) == 1) return v3.length();
	else return dis_point_line(P, A, B);
}


double Area(Polygon& Pnt, int n){
	double area = 0;
	for (int i = 1; i < n - 1; ++i)
		area += (Pnt[i] - Pnt[0]).det(Pnt[i+1] - Pnt[0]);
	return area * 0.5;
}
struct Line{
	/*
	 Line = p + tv;   p是直线上一个点, v是直线的方向向量
	*/
	Point p;
	Vector v;
	Line(Point A = O, Point B = O):p(A), v(B-A){
	}//这里方向向量 B-A 和 A-B 会影响到getpoint 
	Point getpoint(double t) const{return p + v*t;}
};

struct Circle{
	Point c;
	double r;
	Circle(Point _c = O, double _r = 0):c(_c), r(_r){
	}
	Point getpoint(double a){
		return Point(c.x + cos(a) * r, c.y + sin(a) * r);
	}
	double area(double ang){
		return 0.5 * r * r * ang;
	}
};
int intersection_line_circle(Line L, Circle C, double& t1, double& t2, vector<Point>& sol){
	/*
		计算直线与圆的交点
		返回值为交点数量
		sol存储交点
	*/
	double a = L.v.x, b = L.p.x - C.c.x, c = L.v.y, d = L.p.y - C.c.y;
	double e = a*a + c*c, f = 2 * (a*b + c*d), g = b*b + d*d - C.r*C.r;
	double delta = f*f - 4*e*g;
	if (dcmp(delta) < 0) return 0; //相离
	if (dcmp(delta) == 0){
		t1 = t2 = -f / 2 / e;
		sol.push_back(L.getpoint(t1));
		return 1;
	}
	t1 = (-f + sqrt(delta)) / (2*e); sol.push_back(L.getpoint(t1));
	t2 = (-f - sqrt(delta)) / (2*e); sol.push_back(L.getpoint(t2));
	return 2;

}




int in_polygon(Point P, Polygon& poly){
	/*
		判断点是否在多边形内 (可以是凹多边形, 可以自交的多边形)
		返回值为-1表示点在多边形的边上, 1表示在多边形内部, 0表示在多边形的外部
		poly内的点可以是逆时针排序的
		如果多边形是凸多边形的话, 可以简化为判断点是不是在所有边的左边(要求逆时针) 
	*/ 
	int wn = 0; // winding number
	int n = poly.size();
	for (int i = 0; i < n; ++i){
		if (on_seg(P, poly[i], poly[(i+1) % n])) return -1;
		int k = dcmp((poly[(i+1) % n] - poly[i]).det(P - poly[i]));
		int d1 = dcmp(poly[i].y - P.y);
		int d2 = dcmp(poly[(i+1)%n].y - P.y);
		if (k > 0 && d1 <= 0 && d2 > 0) wn++;//注意此处的 = 取不取的到 
		if (k < 0 && d2 <= 0 && d1 > 0) wn--;
	}
	if (wn) return 1;
	return 0;
}
bool in_circle(Point P, Circle C){
	double d = (P-C.c).length();
	return dcmp(d-C.r) < 0; 
}
inline double angle(Vector v) {
	/*
		返回向量的极角
		与圆相关的题中会大量用到
		返回值为(-pi, pi] 
	*/
	double ret = add(atan2(v.y, v.x), 0); 
	if (dcmp(ret < 0)) ret += 2 * pi;
	return ret;
}
bool cmp(Point A, Point B){
	
	return angle(A) < angle(B);
}
Point Origin; 
int n;
Polygon Pnt;
double limit;
double Area(Point A, Point B, Point C){
	vector<Point> Pnt;
	Pnt.push_back(A);
	Pnt.push_back(B);
	Pnt.push_back(C);
	return fabs(Area(Pnt, 3));
}
double angle(Vector A, Vector B){
	return acos(A.dot(B) / A.length() / B.length());
}
double Compute(Circle C, Point a, Point b){
	if (dcmp((C.c - a).det(a-b)) == 0) return 0;
	int sig = dcmp((a-C.c).det(b-C.c));
	double da = (a-C.c).length(), db = (b-C.c).length();
//	cout << da << ' ' << db << endl;
	if (da > db){
		swap(da, db);
		swap(a, b);
	}
	double t1, t2;
	vector<Point> sol;
	int cnt = intersection_line_circle(Line(a, b), C, t1, t2, sol), k = 0;
	if (cnt == 2 && (sol[0]-b).length() > (sol[1]-b).length()) k = 1;
	
	if (dcmp(da - C.r) <= 0){
		if (dcmp(db - C.r) <= 0) return sig * Area(C.c, a, b);
		
		
		double ret = Area(C.c, a, sol[k]) + C.area(angle(sol[k]-C.c, b-C.c));
		return ret * sig;
	}
	double dis = dis_point_seg(C.c, a, b);
	if (dcmp(dis - C.r) >= 0) return sig * C.area(angle(a-C.c, b-C.c));
	double k1 = C.r / (C.c - b).length(), k2 = C.r / (C.c - a).length();
	Point c = C.c + (a-C.c) * k2, d = C.c + (b-C.c) * k1;
	double ret = C.area(angle(c-C.c, d-C.c)) - (C.area(angle(sol[0]-C.c, sol[1]-C.c)) - Area(C.c, sol[0], sol[1]));
	return ret * sig;	
}
double Compute(double r){
	if (dcmp(r) == 0) return 0; 
	double ret = 0;
	for (int i = 0; i < n; ++i){
		ret += Compute(Circle(Origin, r), Pnt[i], Pnt[(i+1) % n]);
	}
		
	return ret;
}
int main(){
//必须编译过才能交
//	ios::sync_with_stdio(false);
//	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);
//	
	int ik = 1, i, j, k, kase;
	scanf("%d", &kase); 
	while(kase--){
		scanf("%d", &n);
		Pnt.resize(n);
		for (i = 0 ; i < n; ++i) Pnt[i].read();
		Origin.read();
 		scanf("%lf", &limit);
		limit = limit / 100 * Area(Pnt, n);
		double lft = 0, rght = 10000;
		while(lft + eps < rght){
			double mid = (lft + rght) / 2;
			double area = Compute(mid);
//			cout << mid << ' ' << area << endl;
			if (dcmp(area - limit) >= 0) rght = mid;
			else lft = mid;
		}
		printf("Case %d: %.0f\n", ik++, lft);
	}
	return 0;
}
/*
4 3
-1 1
-1 -1
1 -1
1 1
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值