【半平面交】poj2540 Hotter Colder

写了三天的半平面交委屈总觉得自己姿势有问题。。



#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm> 

#define eps 1e-8

using namespace std;

struct Point {
    double x, y;
    Point(double _x = 0. , double _y = 0. ) : x(_x), y(_y) {};
}Ext[65];

struct Vector {
    Point s, e;
    double k, d; // 极角 截距
}V[65];
int tot = 0;

inline int dcmp(double x) { if (x < -eps) return -1; return x > eps; }
inline double Cross(double x1, double y1, double x2, double y2)
{
    return x1 * y2 - x2 * y1;
}
inline double Cross(Point O, Point A, Point B)
{ // 向量 OA 与 OB 的叉积
	return (A.x - O.x) * (B.y - O.y) - (B.x - O.x) * (A.y - O.y);  
}

inline void SetLine(double x1, double y1, double x2, double y2, Vector &v)
{ // 向量左边为有效平面
    v.s = Point(x1, y1); v.e = Point(x2, y2);
    v.k = atan2(y2 - y1, x2 - x1);
    if (dcmp(x1 - x2)) v.d = Cross(x1, y1, x2, y2) / fabs(x1 - x2);
    else v.d = Cross(x1, y1, x2, y2) / fabs(y1 - y2);
}

inline Point Revolve(double cosA, double sinA, Point A, Point B)
{ // 将向量 AB 逆时针旋转角度A
    Point B_;
    B_.x = (B.x - A.x) * cosA - (B.y - A.y) * sinA + A.x;
    B_.y = (B.x - A.x) * sinA + (B.y - A.y) * cosA + A.y;
    return B_;
}

inline bool VecCmp(const Vector &a,const Vector &b)
{
    if (dcmp(a.k - b.k)) return a.k < b.k;
    return a.d < b.d;
}

inline bool Parallel(Vector a, Vector b)
{ // 向量平行
    double u = Cross(a.s.x - a.e.x, a.s.y - a.e.y, b.s.x - b.e.x, b.s.y - b.e.y);
    return !dcmp(u);
}

Point CrossPoint(Vector a, Vector b)
{ // 向量求交点
    Point res;
    double u = Cross(a.s, a.e, b.s), v = Cross(a.e, a.s, b.e);
    res.x = (b.s.x * v + b.e.x * u) / (u + v);
    res.y = (b.s.y * v + b.e.y * u) / (u + v);
    return res; 
}

inline bool EqualPoint(Point &a, Point &b)
{
    return !dcmp(a.x - b.x) && !dcmp(a.y - b.y);
}

Vector deq[65];

void HalfPanelCross(Vector *v, int &N, Point *p, int &M)
{
    sort(v, v + N, VecCmp); int tn = 1; M = 0;
    for (int i = 1; i < N; ++ i) {
        if (dcmp(v[i].k - v[i - 1].k)) v[tn ++] = v[i];
    }
    N = tn; if (N < 3) return;
    deq[0] = v[0]; deq[1] = v[1];
    int head = 0, tail = 1;
    for (int i = 2; i < N; ++ i) {
        if (Parallel(deq[tail], deq[tail - 1]) || Parallel(deq[head], deq[head + 1])) return;
        while ( head < tail && dcmp( Cross(v[i].s, v[i].e, CrossPoint(deq[tail], deq[tail - 1])) ) < 0 ) -- tail;
        while ( head < tail && dcmp( Cross(v[i].s, v[i].e, CrossPoint(deq[head], deq[head + 1])) ) < 0 ) ++ head;
        deq[++ tail] = v[i];
    }
    while ( head < tail && dcmp( Cross(deq[head].s, deq[head].e, CrossPoint(deq[tail], deq[tail - 1])) ) < 0 ) -- tail;
    while ( head < tail && dcmp( Cross(deq[tail].s, deq[tail].e, CrossPoint(deq[head], deq[head + 1])) ) < 0 ) ++ head;
    
    if (tail < head + 1) return;
    for (int i = head; i < tail; ++ i) {
        p[M ++] = CrossPoint(deq[i], deq[i + 1]);
    }
    p[M ++] = CrossPoint(deq[tail], deq[head]);
    M = (int)(unique(p, p + M, EqualPoint) - p);
}

double PolygonArea(Point *p, int M)
{
    double S = p[0].y * (p[M - 1].x - p[1].x);
    for (int i = 1; i < M; ++ i) {
        S += p[i].y * (p[i - 1].x - p[(i + 1) % M].x);
    }
    return fabs(S * 0.5);
}

int main()
{
    SetLine(0. , 0. , 10. , 0. , V[tot ++]);
    SetLine(0. , 10. , 0. , 0. , V[tot ++]);
    SetLine(10. , 10. , 0. , 10. , V[tot ++]);
    SetLine(10. , 0. , 10. , 10. , V[tot ++]);
    
    Point Last = Point(0. , 0.), Next, Mid, Temp;
    string s; int CntLine = 0;
    ios :: sync_with_stdio(false); bool flag = 0;
    while (cin >> Next.x >> Next.y >> s) {
    	if (s == "Same" || flag) { flag = 1; printf("0.00\n"); continue; }
        Mid = Point((Last.x + Next.x) * 0.5, (Last.y + Next.y) * 0.5);
        if (s == "Colder") Temp = Revolve(0. , 1. , Mid, Next);
        else if (s == "Hotter")Temp = Revolve(0. , -1. , Mid, Next);
        SetLine(Mid.x, Mid.y, Temp.x, Temp.y, V[tot ++]);
        
        HalfPanelCross(V, tot, Ext, CntLine);
        
        if (CntLine < 3) { flag = 1; printf("0.00\n"); }
        else printf("%.2f\n", PolygonArea(Ext, CntLine));
        Last = Next;
    }
    
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值