半平面交
裸的,注意事项在代码里。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define N 505
using namespace std;
namespace runzhe2000
{
typedef double db;
const db eps = 1e-5;
struct point
{
db x, y;
point operator + (const point &that) const {return (point){x + that.x, y + that.y};}
point operator - (const point &that) const {return (point){x - that.x, y - that.y};}
db operator * (const point &that) const {return x*that.y-y*that.x;}
point operator * (const db &v) const {return (point){x*v, y*v};}
}p[N];
struct line
{
point p, v; db alpha;
bool operator < (const line &that) const {return alpha < that.alpha;}
}l[N], q[N];
int n, lcnt, head, tail;
point get_cp(line l1, line l2)
{
/*
精妙的实现。考虑通过面积比得到高的比,再得到线段的比。注意叉乘别反了。
*/
point u = l1.p - l2.p; db base = (l2.v * u) / (l1.v * l2.v);
return l1.p + l1.v * base;
}
bool onlef(point p, line l)
{
point u = p-l.p; return l.v * u > 0;
}
void HP()
{
sort(l+1, l+1+lcnt);
for(int i = 1; i <= lcnt; i++)
{
for(; tail - head >= 2 && !onlef(get_cp(q[tail-1], q[tail-2]), l[i]); tail--);
if(tail - head >= 1 && fabs(l[i].alpha - q[tail-1].alpha) < eps) q[tail-1] = onlef(q[tail-1].p, l[i]) ? q[tail-1] : l[i]; // 考虑直线的斜率相等
else q[tail++] = l[i];
}
for(;tail-head>2;) //把头尾多余的半平面不断删除掉
{
if(!onlef(get_cp(q[head], q[head+1]), q[tail-1])) head++;
else if(!onlef(get_cp(q[tail-1], q[tail-2]), q[head])) tail--;
else break;
}
if(tail-head <= 2) {puts("0.000\n");exit(0);}
/*
无解当且仅当做到某一个半平面时,该半平面与之前的半平面交产生矛盾,那此时一定已经至少转了180度,因而这时候把这些半平面交出队之后不可能围成无界图形。
因为这题保证了围成的面积是有界的,所以可以直接通过判无界来判断无解。
对于更一般的图,解不一定是有界的,也许可以采用在外面套上一圈巨大的半平面来制约它,强行有界。
*/
}
void main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
int cnt; scanf("%d",&cnt);
for(int j = 1; j <= cnt; j++) scanf("%lf%lf",&p[j].x,&p[j].y); p[cnt+1] = p[1];
for(int j = 1; j <= cnt; j++) ++lcnt, l[lcnt].p = p[j], l[lcnt].v = p[j+1] - p[j], l[lcnt].alpha = atan2(l[lcnt].v.y, l[lcnt].v.x);
}
HP(); q[tail] = q[head]; int cnt = 0;
for(int i = head; i < tail; i++) p[++cnt] = get_cp(q[i], q[i+1]);
p[cnt+1] = p[1];
db ans = 0; for(int i = 1; i <= cnt; i++) ans += p[i] * p[i+1];
printf("%.3lf\n",ans/2);
}
}
int main()
{
runzhe2000::main();
}
UPD(20170715):复习一下,再敲一遍,注释里是新的理解
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 505
using namespace std;
namespace runzhe2000
{
typedef double db;
const db eps = 1e-7, inf = 1e9;
struct point
{
db x, y;
db operator * (const point &that) const {return x*that.y-y*that.x;}
point operator - (const point &that) const {return (point){x-that.x, y-that.y};}
point operator + (const point &that) const {return (point){x+that.x, y+that.y};}
point operator * (db v) const {return (point){x*v, y*v};}
}p[N];
struct line
{
point p, v;
db deg;
bool operator < (const line &that) const
{
return fabs(deg - that.deg) < eps ? (that.p-p)*v > 0 : deg < that.deg;
}
}l[N], q[N];
point get_cp(line a, line b)
{
point u = a.p - b.p; db v = b.v * u / (a.v * b.v);
return a.p + a.v*v;
}
bool onlef(point p, line l)
{
return l.v*(p-l.p) > 0;
}
int n, cnt;
void main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
{
int m; scanf("%d",&m);
for(int j = 1; j <= m; j++)
scanf("%lf%lf",&p[j].x,&p[j].y);
p[m+1] = p[1];
for(int j = 1; j <= m; j++)
{
l[++cnt].p = p[j];
l[cnt].v = p[j+1] - p[j];
l[cnt].deg = atan2(l[cnt].v.y, l[cnt].v.x);
}
}
l[++cnt].p = (point){ inf, inf}, l[cnt].v = (point){-inf, 0}, l[cnt].deg = atan2(l[cnt].v.y, l[cnt].v.x);
l[++cnt].p = (point){ inf, -inf}, l[cnt].v = (point){0, inf}, l[cnt].deg = atan2(l[cnt].v.y, l[cnt].v.x);
l[++cnt].p = (point){-inf, inf}, l[cnt].v = (point){0, -inf}, l[cnt].deg = atan2(l[cnt].v.y, l[cnt].v.x);
l[++cnt].p = (point){-inf, -inf}, l[cnt].v = (point){ inf, 0}, l[cnt].deg = atan2(l[cnt].v.y, l[cnt].v.x);
/*
增加大边界,虽然这题并不一定要加。
*/
sort(l+1, l+1+cnt); int tmp = 1;
for(int i = 2; i <= cnt; i++)
if(fabs(l[i].deg - l[tmp].deg) > eps) l[++tmp] = l[i];
cnt = tmp;
/*
去掉斜率相等的直线,以防有问题。
*/
q[0] = l[1], q[1] = l[2]; int head = 0, tail = 2;
for(int i = 3; i <= cnt; i++)
{
for(; tail - head >= 2 && !onlef(get_cp(q[tail-1], q[tail-2]), l[i]); tail--);
q[tail++] = l[i];
}
for(; tail - head > 2; )
{
if(!onlef(get_cp(q[tail-1], q[tail-2]), q[head])) tail--;
else if(!onlef(get_cp(q[head], q[head+1]), q[tail-1])) head++;
else break;
}
if(tail - head <= 2) puts("0.000");
/*
无解当且仅当做到某一个半平面时,该半平面与之前的半平面交产生矛盾,那此时一定已经至少转了180度,因而这时候把这些半平面交出队之后不可能围成无界图形。
因为这题保证了围成的面积是有界的,所以可以直接通过判无界来判断无解。对于更一般的图,解不一定是有界的,也许可以采用在外面套上一圈大半平面来制约它,强行有界。
至于为什么只需要判断队列里是否只剩2个以下的半平面,而不存在多个(>2)半平面围出一个无界半平面的情况。不知道,想了半天也不会证。
*/
else
{
int c = 0; q[tail] = q[head];
for(int i = head; i < tail; i++) p[++c] = get_cp(q[i], q[i+1]);
p[0] = p[c]; db ans = 0;
for(int i = 1; i <= c; i++) ans += p[i-1]*p[i];
printf("%.3lf\n",ans/2);
}
}
}
int main()
{
runzhe2000::main();
}