题意:给一个复杂多边形(可以自交),求闭合部分面积总和,一个点在闭合米部分的定义是,从它开始无论怎么走都无法不经过其他点到达无穷远处。
链接:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2448
解法:假设我们得到的简单多边形,那么直接用叉积可以求面积。现在如何将复杂的转化为简单的,只需求复杂边形的轮廓即可,方法是,先重建图,对于每个相邻点,建无向边,得到一个没有重边,没有重点,没有自环的简单图,然后,找到一个位置在最左下的点为起点p,再找到p出去的,向量角度值最小的边,以它为开始,求出这个图的轮廓即可。。注意,求出的轮廓终点也一定为p,这样子对于看上去不闭合的图,求出的多边形也闭合了,这样子不闭合的面积就会被算为0。。
#include<cmath>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
const double eps = 1e-6, pi = acos(-1.0);
int sgn(double x){
if(abs(x) < eps) return 0;
else return x > 0 ? 1 : -1;
}
struct Point{
double x, y;
Point(){};
Point(double x1, double y1){
x = x1, y = y1;
}
};
typedef Point Vector;
Vector operator + (Vector a, Vector b){
return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b){
return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double b){
return Vector(a.x * b, a.y * b);
}
double operator * (Vector a, Vector b){
return a.x * b.x + a.y * b.y;
}
double operator % (Vector a, Vector b){
return a.x * b.y - a.y * b.x;
}
bool operator == (Point a, Point b){
return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
}
#define MAXN 100010
#define N 200
int n;
Point p[200], pv[MAXN];
struct Edge{
int next, to;
}e[MAXN << 1];
int head[MAXN], ne, nv;
void add(int a, int b){
int t = ++ne;
e[t].to = b, e[t].next = head[a];
head[a] = t;
}
bool inter(Point p1, Point p2, Point q1, Point q2){
if(sgn((p1 - p2) % (q1 - q2)) == 0 ) return false;
int t1 = sgn((p2 - p1) % (q1 - p1)) * sgn((p2 - p1) % (q2 - p1));
int t2 = sgn((q2 - q1) % (p1 - q1)) * sgn((q2 - q1) % (p2 - q1));
return t1 <= 0 && t2 <= 0;
}
Point getp(Point p1, Vector v1, Point p2, Vector v2){
double t = ((p2 - p1) % v2) / (v1 % v2);
return p1 + v1 * t;
}
vector<Point>seg[N], vp;
bool operator < (const Point a, const Point b){
if(sgn(a.x - b.x) != 0) return sgn(a.x - b.x) < 0;
else return sgn(a.y - b.y) < 0;
}
map<Point, int>mto;
set<pair<int,int> >se;
void build_edge(){
for(int i = 0; i < n - 1; i++){
seg[i].push_back(p[i]);
seg[i].push_back(p[i + 1]);
vp.push_back(p[i]);
vp.push_back(p[i + 1]);
for(int j = 0; j < n - 1; j++){
if(inter(p[i], p[i+1], p[j], p[j+1]) == 0) continue;
Point x = getp(p[i], p[i+1] - p[i], p[j], p[j+1] - p[j]);
seg[i].push_back(x);
vp.push_back(x);
}
}
sort(vp.begin(), vp.end());
nv = 0;
for(int i = 0; i < vp.size(); i++){
if(i > 0 && vp[i] == vp[i-1]) continue;
mto[vp[i]] = ++nv;
pv[nv] = vp[i];
}
for(int i = 1; i <= nv; i++) head[i] = -1;
ne = 0;
for(int i = 0; i < n - 1; i++){
sort(seg[i].begin(), seg[i].end());
for(int j = 1; j < seg[i].size(); j++){
if(j > 0 && seg[i][j] == seg[i][j-1]) continue;
int a = mto[seg[i][j]];
int b = mto[seg[i][j-1]];
if(a > b) swap(a, b);
pair<int,int>pa = make_pair(a, b);
if(se.find(pa) != se.end()) continue;
se.insert(pa);
add(a, b);
add(b, a);
}
}
}
double angle(Vector v1, Vector v2){
double a = atan2(v1.y, v1.x) - atan2(v2.y, v2.x);
while(sgn(a) < 0) a += 2 * pi;
while(sgn(a - 2 * pi) >= 0) a -= 2 * pi;
a = min(a, 2 * pi - a);
return a;
}
double cal(Vector v1, Vector v2){
if(sgn(v1 % v2) == 0)
{
if(sgn(v1 * v2) > 0) return 0;
else return -pi;
}
else{
double a = angle(v1, v2);
if(sgn(v1 % v2) > 0) a = - a;
return a;
}
}
int findone(int w, int v){
double maxi = -100;
Vector v1 = pv[v] - pv[w];
int ans = 0;
for(int i = head[v]; i != -1; i = e[i].next){
int to = e[i].to;
Vector v2 = pv[to] - pv[v];
double a = cal(v1, v2);
if(sgn(a - maxi ) > 0){
maxi = a;
ans = to;
}
}
return ans;
}
vector<Point>route;
void solve(){
int w = 1;
Point minp = Point(1e9, 1e9);
for(int i = 1; i <= nv; i++){
if(pv[i] < minp){
w = i;
minp = pv[i];
}
}
int v = 1;
double mini = 1e9;
for(int i = head[w]; i != -1; i = e[i].next){
int to = e[i].to;
Vector v1 = pv[to] - pv[w];
double a = atan2(v1.y, v1.x);
if(sgn(a - mini) < 0){
mini = a;
v = to;
}
}
int w1 = w;
//just go
while(true){
route.push_back(pv[w]);
int newv = findone(w, v);
w = v;
v = newv;
if(w == w1) break;
}
}
int main(){
scanf("%d",&n);
for(int i = 0; i < n; i++){
int x, y;
scanf("%d%d",&x,&y);
p[i] = Point(x, y);
}
build_edge();
solve();
//cal ans
double ans = 0.0;
for(int i = 0; i < route.size(); i++){
int j = i + 1 == route.size() ? 0 : i + 1;
ans += route[i] % route[j];
}
ans = abs(ans);
ans = ans * 0.5;
printf("%.10f\n",ans);
return 0;
}