逆时针给出n个凸多边形的顶点坐标,求它们交的面积。例如n=2时,两个凸多边形如下图:
则相交部分的面积为5.233。
【输入格式】
输入文件polygon.in第一行有一个整数n,表示凸多边形的个数,以下依次描述各个多边形。第i个多边形的第一行包含一个整数mi,表示多边形的边数,以下mi行每行两个整数,逆时针给出各个顶点的坐标。
【输出格式】
输出文件仅包含一个实数,表示相交部分的面积,保留三位小数。
【样例】
polygon.in
2
6
-2 0
-1 -2
1 -2
2 0
1 2
-1 2
4
0 -3
1 -1
2 2
-1 0
polygon.out
5.233
【限制】
50%的数据满足:n=2
100%的数据满足:2<=n<=10,3<=mi<=50,每维坐标为[-1000,1000]内的整数
朴素的算法:先定出两个凸多边形的交,再加入一个多边形,再求交,直到将最后一个多边形都包含进去为止。求两个多边形的交的方法:枚举每两条边,找出所有的交点,再在这些点(包含原来多边形的顶点)中找出同时属于两个多边形内部的点,按极角排好序后即为所求的凸多边形的交。
判线段相交和求线段交点,都用叉乘,具体见程序。
Accode:
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <cmath>
const char fi[] = "polygon.in";
const char fo[] = "polygon.out";
const int maxN = 20, maxM = 210;
const double zero = 1e-8;
struct vec
{
double x, y;
vec():x(0), y(0) {}
vec(double x, double y): x(x), y(y) {}
vec operator-() const {return vec(-x, -y);}
vec operator-(const vec b) const
{return vec(x - b.x, y - b.y);}
vec operator+(const vec b) const
{return vec(x + b.x, y + b.y);}
double operator*(const vec b) const
{return x * b.y - y * b.x;}
vec operator*(const double d) const
{return vec(x * d, y * d);}
double norm() const {return x * x + y * y;}
};
vec p[maxN][maxM];
vec res[maxM], tmp1[maxM], tmp2[maxM];
int n, m[maxM], _tmp1, _tmp2, _res;
void init_file()
{
freopen(fi, "r", stdin);
freopen(fo, "w", stdout);
return;
}
inline int getint()
{
int res = 0; char tmp; bool sgn = 1;
do tmp = getchar();
while (!isdigit(tmp) && tmp - '-');
if (tmp == '-') {sgn = 0; tmp = getchar();}
do res = (res << 3) + (res << 1) + tmp - '0';
while (isdigit(tmp = getchar()));
return sgn ? res : -res;
}
void readdata()
{
n = getint();
for (int i = 0; i < n; ++i)
{
m[i] = getint();
for (int j = 0; j < m[i]; ++j)
{
int x = getint(), y = getint();
p[i][j] = vec((double)x, (double)y);
}
p[i][m[i]] = p[i][0];
}
return;
}
inline int sgn(double x)
{return x > zero ? 1 : x < -zero ? -1 : 0;}
inline bool cross(const vec &A, const vec &B,
const vec &C, const vec &D)
{
vec AC = C - A, AD = D - A,
BC = C - B, BD = D - B;
return (sgn(AC * AD) ^ sgn(BC * BD)) == -2
&& (sgn(AC * BC) ^ sgn(AD * BD)) == -2;
} //判断线段AB和CD相交。
inline vec jndm(const vec &A, const vec &B,
const vec &C, const vec &D)
{
vec AB = B - A, CD = D - C,
AC = C - A, AD = D - A;
return A + AB * fabs((AC * AD) / (AB * CD));
} //求出线段AB和CD的交点的坐标。
inline bool outof(const vec &O, const vec *p,
const int m)
{
for (int i = 0; i < m; ++i)
if (sgn((p[i] - p[i + 1]).norm()))
if (sgn((p[i] - O) * (p[i + 1] - O)) < 0)
return 1;
return 0;
} //判断O是否在凸m边形p外。
inline int cmp(const void *a, const void *b)
{
vec B = *(vec *)b - tmp2[0],
A = *(vec *)a - tmp2[0];
if (sgn(B * A)) return sgn(B * A);
//极角不同则按极角排序。
return sgn(A.norm() - B.norm());
//极角相同则按模长从小到大排序。
}
void work()
{
for (int j = 0; j < m[0] + 1; ++j)
res[j] = p[0][j];
_res = m[0];
for (int i = 1; i < n; ++i)
{
_tmp1 = 0;
for (int j = 0; j < _res; ++j)
tmp1[_tmp1++] = res[j];
for (int j = 0; j < m[i]; ++j)
tmp1[_tmp1++] = p[i][j];
for (int j = 0; j < _res; ++j)
for (int k = 0; k < m[i]; ++k)
if (cross(res[j], res[j + 1],
p[i][k], p[i][k + 1]))
tmp1[_tmp1++] =
jndm(res[j], res[j + 1],
p[i][k], p[i][k + 1]);
_tmp2 = 0;
for (int j = 0; j < _tmp1; ++j)
if (!outof(tmp1[j], res, _res) &&
!outof(tmp1[j], p[i], m[i]))
tmp2[_tmp2++] = tmp1[j];
if (_tmp2 > 0)
qsort(tmp2 + 1, _tmp2 - 1,
sizeof tmp2[0], cmp);
else {_res = 0; break;}
//要有交点才能排序,否则直接退出。
_res = _tmp2;
for (int j = 0; j < _tmp2; ++j)
res[j] = tmp2[j];
res[_res] = res[0];
}
double ans = 0;
for (int i = 1; i < _res - 1; ++i)
ans += fabs((res[i + 1] - res[0])
* (res[i] - res[0]));
printf("%.3lf\n", ans / 2);
return;
}
int main()
{
init_file();
readdata();
work();
return 0;
}