ZOJ1886/POJ2540 Hotter Colder(半平面交)

题意是,有一个二维(0,0)到(10,10)的区域,一个人从(0,0)开始一次拜访区域内的点,去找一样东西(就在这个区域内),每次他到达一个新的点后,都会有一句话,来告诉你是离那个东西更近了(Hotter),还是更远了(Colder),或者距离不变(Same),然后让你算出,这时那个东西可能处在的位置的区域的面积

明显的半平面交,每次取当前点和下一个点的中垂线向量加入多边形边集(当然,必须注意方向,根据距离的增加或者减小可以判断),半平面交的面积就好了


代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

const double eps = 1e-8;
const double pi = acos(-1.0);
const int N = 60;
const double maxl = 10;

struct cpoint {//C++构造函数,默认缺省值为(0,0)
    double x, y;
    cpoint(double xx = 0, double yy = 0): x(xx), y(yy) {};
};

int dcmp(double x) {//判断参数的符号,负数返回-1,0返回0,正数返回1
    if (x < -eps) return -1; 
	else return x > eps;
}

double cross(cpoint p0, cpoint p1, cpoint p2) { // p0p1 与 p0p2 叉积
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

bool EqualPoint(cpoint a, cpoint b) {//两点相等
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

struct cvector {//向量
    cpoint s, e;
    double ang, d;
};

//atan (double x)弧度表示的反正切
//atan2(double x,double y)弧度表示的反正切,相当于atan(x/y)

void setline(double x1, double y1, double x2, double y2, cvector &v) {
    v.s.x = x1; v.s.y = y1;
    v.e.x = x2; v.e.y = y2;
    v.ang = atan2(y2 - y1, x2 - x1);
	//这里的 d 代表向量(直线)和坐标轴的截距,正数表示 原点 在该向量的左边
	//(这道题要求左半平面交),负号则表示 原点 在右边
    if (dcmp(x1 - x2))          // x1 > x2
		v.d = (x1 * y2 - x2 * y1) / fabs(x1 - x2);
    else v.d = (x1 * y2 - x2 * y1) / fabs(y1 - y2);
}

//判向量平行
bool parallel(const cvector &a, const cvector &b) {
    double u = (a.e.x - a.s.x) * (b.e.y - b.s.y) - (a.e.y - a.s.y) * (b.e.x - b.s.x);
    return dcmp(u) == 0;
}

//求两向量(直线)交点 (两向量不能平行或重合)
cpoint CrossPoint(const cvector &a, const cvector &b) {
    cpoint 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;
}

//半平面交排序函数[优先顺序: 1.极角 2.前面的直线在后面的左边]
static bool VecCmp(const cvector &l, const cvector &r) {
    if (dcmp(l.ang - r.ang)) return l.ang < r.ang;
    return l.d < r.d;
}

cvector deq[N]; //用于计算的双端队列

void HalfPanelCross(cvector vec[], int n, cpoint cp[], int &m) {
    int i, tn; m = 0;
    sort(vec, vec + n, VecCmp);
    for(i = tn = 1; i < n; ++i) //平面在向量左边的筛选,方向相同的筛去,相反的不能筛去
        if(dcmp(vec[i].ang - vec[i - 1].ang) != 0)
            vec[tn++] = vec[i];
		n = tn;
		int bot = 0, top = 1;
		deq[0] = vec[0];
		deq[1] = vec[1]; // vec[]大小不可小于2
		for (i = 2; i < tn; ++i) 
		{
			if (parallel(deq[top], deq[top - 1]) || 
				parallel(deq[bot], deq[bot + 1]) ) return ;
			while ( bot < top && dcmp( cross(vec[i].s, vec[i].e, 
				CrossPoint(deq[top], deq[top - 1])) ) < 0 ) top--;
			while ( bot < top && dcmp( cross(vec[i].s, vec[i].e, 
				CrossPoint(deq[bot], deq[bot + 1])) ) < 0 ) bot++;
			deq[++top] = vec[i];
		}
		while ( bot < top && dcmp( cross(deq[bot].s, deq[bot].e, 
			CrossPoint(deq[top], deq[top - 1])) ) < 0 ) top--;
		while ( bot < top && dcmp( cross(deq[top].s, deq[top].e, 
			CrossPoint(deq[bot], deq[bot + 1])) ) < 0 ) bot++;

		if (top <= bot + 1) return ; // 两条或两条以下的直线,面积无穷大

		for (i = bot; i < top; i ++)
			cp[m++] = CrossPoint(deq[i], deq[i + 1]);
		if (bot < top + 1)
			cp[m++] = CrossPoint(deq[bot], deq[top]);
		m = unique(cp, cp + m, EqualPoint) - cp;
}

double PolygonArea(cpoint p[], int n) {
    if (n < 3) return 0;
    double s = p[0].y * (p[n - 1].x - p[1].x);
    for (int i = 1; i < n; ++i)
        s += p[i].y * (p[i - 1].x - p[(i + 1) % n].x);
    return fabs(s / 2); // 顺时针方向s为负
}


//向量的旋转 //底边线段ab 绕a逆时针旋转角度A,b->b1,sinl是sinA的值。 
cpoint Whirl(double cosl, double sinl, cpoint a, cpoint b)
{    
	b.x -= a.x; b.y -= a.y;
    cpoint c;
    c.x = b.x * cosl - b.y * sinl + a.x;
    c.y = b.x * sinl + b.y * cosl + a.y;
    return c;
}


int n, m;
cvector v[N];
cpoint cp[N],cur,next;

void solve() {

	cpoint mid;
	char str[10];
    
	scanf("%s",str);

	mid.x=(cur.x+next.x)/2; mid.y=(cur.y+next.y)/2;
	cpoint kp;
    if(strcmp(str,"Colder")==0)
		kp=Whirl(0,1,mid,next);
	else if(strcmp(str,"Hotter")==0)
		kp=Whirl(0,-1,mid,next);
	
	cur=next;

	setline(mid.x,mid.y,kp.x,kp.y,v[n++]);

    HalfPanelCross(v, n, cp, m);

    if (m < 3) printf("0.00\n");
    else printf("%.2lf\n", PolygonArea(cp, m));
}

int main() 
{
	cur.x = cur.y = 0.0;
	setline(0, 0, maxl, 0, v[0]);//v[0] ~ v[3] 是点集的外包矩阵
    setline(maxl, 0, maxl, maxl, v[1]);
    setline(maxl, maxl, 0, maxl, v[2]);
    setline(0, maxl, 0, 0, v[3]);
    n = 4;

    while (~scanf("%lf%lf",&next.x,&next.y)){
        solve();
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值