[半平面交][贪心]Feng Shui POJ - 3384

Feng shui is the ancient Chinese practice of placement and arrangement of space to achieve harmony with the environment. George has recently got interested in it, and now wants to apply it to his home and bring harmony to it.

There is a practice which says that bare floor is bad for living area since spiritual energy drains through it, so George purchased two similar round-shaped carpets (feng shui says that straight lines and sharp corners must be avoided). Unfortunately, he is unable to cover the floor entirely since the room has shape of a convex polygon. But he still wants to minimize the uncovered area by selecting the best placing for his carpets, and asks you to help.

You need to place two carpets in the room so that the total area covered by both carpets is maximal possible. The carpets may overlap, but they may not be cut or folded (including cutting or folding along the floor border) — feng shui tells to avoid straight lines.

Input

The first line of the input file contains two integer numbers n and r — the number of corners in George’s room (3 ≤ n ≤ 100) and the radius of the carpets (1 ≤ r ≤ 1000, both carpets have the same radius). The following n lines contain two integers xi and yi each — coordinates of the i-th corner (−1000 ≤ xiyi ≤ 1000). Coordinates of all corners are different, and adjacent walls of the room are not collinear. The corners are listed in clockwise order.

Output

Write four numbers x1, y1, x2, y2 to the output file, where (x1, y1) and (x2, y2) denote the spots where carpet centers should be placed. Coordinates must be precise up to 4 digits after the decimal point.

If there are multiple optimal placements available, return any of them. The input data guarantees that at least one solution exists.

Sample Input

 
#15 2
-2 0
-5 3
0 8
7 3
5 0
#24 3
0 0
0 8
10 8
10 0

Sample Output

 
#1-2 3 3 2.5
#23 5 7 3

Hint

题意: 给出一个凸多边形,以及圆的半径,求在多边形内部覆盖最大面积的两圆坐标。如上图所示。

分析: 要想两圆覆盖面积最大,那么这两圆相交面积应该最小,也就是说两圆应该尽可能远离,贪心地想,两圆一定与至少两条边相切。此时把凸多边形的各边向内推进R个距离,R为圆的半径,这个过程可以通过求半平面交实现,由于圆一定与两边相切,圆心也就在新构成的凸多边形的顶点上,因为两圆要尽可能远离,两圆心也就位于最远的两顶点处,n^2暴力枚举即可。·

附样例1示意图一张:

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
const int maxp = 1010;
int sgn(double x)
{
	if(fabs(x) < eps)return 0;
	if(x < 0)return -1;
	else return 1;
}
struct Point
{
	double x, y;
	Point(){}
	Point(double _x,double _y){x = _x, y = _y;}
	void input(){scanf("%lf%lf",&x,&y);}
	void output(){printf("%.2f %.2f\n",x,y);}
	bool operator == (Point b)const{return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;}
	bool operator < (Point b)const{return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;}
	Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);}
	//叉积
	double operator ^(const Point &b)const{return x*b.y - y*b.x;}
	//点积
	double operator *(const Point &b)const{return x*b.x + y*b.y;}
	//返回长度
	double len(){return hypot(x,y);/*库函数*/}
	//返回长度的平方
	double len2(){return x*x + y*y;}
	//返回两点的距离
	double distance(Point p){return hypot(x-p.x,y-p.y);}
	Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);}
	Point operator *(const double &k)const{return Point(x*k,y*k);}
	Point operator /(const double &k)const{return Point(x/k,y/k);}
}; 
struct Line
{
	Point s,e;
	Line(){}
	Line(Point _s,Point _e){s = _s, e = _e;}
	bool operator ==(Line v){return (s == v.s)&&(e == v.e);}
	//`根据一个点和倾斜角angle确定直线,0<=angle<pi`
	Line(Point p,double angle)
	{
		s = p;
		if(sgn(angle-pi/2) == 0){e = (s + Point(0,1));}
		else{e = (s + Point(1,tan(angle)));}
	}
	void input()
	{
		s.input();
		e.input();
	}
	bool parallel(Line v){return sgn((e-s)^(v.e-v.s)) == 0;/*两向量叉积为0*/ }
	Point crosspoint(Line v)
	{
		double a1 = (v.e-v.s)^(s-v.s);
		double a2 = (v.e-v.s)^(e-v.s);
		return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
	}
};
struct halfplane:public Line
{
	double angle;
	halfplane(){}
	//`表示向量s->e逆时针(左侧)的半平面`
	halfplane(Point _s,Point _e)
	{
		s = _s;
		e = _e;
	}
	halfplane(Line v)
	{
		s = v.s;
		e = v.e;
	}
	void calcangle(){angle = atan2(e.y-s.y,e.x-s.x);}
	bool operator <(const halfplane &b)const{return angle < b.angle;}
};
struct polygon
{
	int n;
	Point p[maxp];
	Line l[maxp];
	double getarea()
	{
		double sum = 0;
		for(int i = 0;i < n;i++){
			sum += (p[i]^p[(i+1)%n]);
		}
		return fabs(sum)/2;
	}
};
struct halfplanes
{
	int n;//需要输入 
	halfplane hp[maxp];//需要输入,且封闭区域都在向量逆时针方向 
	Point p[maxp];
	int que[maxp];
	int st,ed;//队列的头尾指针,且下标从0开始,指向元素就是头和尾 
	void push(halfplane tmp){hp[n++] = tmp;}
	//去重
	void unique()
	{
		int m = 1;
		for(int i = 1;i < n;i++)
		{
			if(sgn(hp[i].angle-hp[i-1].angle) != 0)
				hp[m++] = hp[i];
			//去除极角相同的情况下,位置在右边(沿向量方向)的边 
			else if(sgn( (hp[m-1].e-hp[m-1].s)^(hp[i].s-hp[m-1].s) ) > 0)
				hp[m-1] = hp[i];
		}
		n = m;
	}
	bool halfplaneinsert()//判断半平面交是否存在 
	{
		for(int i = 0;i < n;i++)hp[i].calcangle();
		sort(hp,hp+n);//先对倾斜角排序 
		unique();
		que[st=0] = 0;
		que[ed=1] = 1;
		p[1] = hp[0].crosspoint(hp[1]);
		for(int i = 2;i < n;i++){
			while(st<ed && sgn((hp[i].e-hp[i].s)^(p[ed]-hp[i].s))<0)ed--;
			while(st<ed && sgn((hp[i].e-hp[i].s)^(p[st+1]-hp[i].s))<0)st++;
			que[++ed] = i;
			if(hp[i].parallel(hp[que[ed-1]]))return false;
			p[ed]=hp[i].crosspoint(hp[que[ed-1]]);
		}
		while(st<ed && sgn((hp[que[st]].e-hp[que[st]].s)^(p[ed]-hp[que[st]].s))<0)ed--;
		while(st<ed && sgn((hp[que[ed]].e-hp[que[ed]].s)^(p[st+1]-hp[que[ed]].s))<0)st++;
		if(st+1>=ed)return false;//最后剩下小于三条直线,表明半平面交不存在 
		return true;
	}
	void getconvex(polygon &con)
	{
		p[st] = hp[que[st]].crosspoint(hp[que[ed]]);
		con.n = ed-st+1;
		for(int j = st,i = 0;j <= ed;i++,j++)
			con.p[i] = p[j];
	}
};

signed main()
{
	int n;
	double R;
	cin >> n >> R; 
	Point p[maxp];
	for(int i = n-1; i >= 0; i--)
		p[i].input();
	halfplanes a;
	a.n = 0;
	for(int i = 0; i < n; i++)
	{
		halfplane temp(p[i], p[(i+1)%n]);
		a.push(temp);
	}
	for(int i = 0; i < n; i++)
	{
		Point vec = p[(i+1)%n]-p[i];
		vec = Point(-vec.y/vec.len(), vec.x/vec.len());
		halfplane temp(p[i]+vec*R, p[(i+1)%n]+vec*R);//向内平移R长度 
		a.hp[i] = temp;
	}
	if(a.halfplaneinsert())
	{
		polygon b;
		a.getconvex(b);
		int ans1, ans2;
		double max_dis = -1e20;
		for(int i = 0; i < b.n; i++)
			for(int j = i+1; j < b.n; j++)
			{
				Point temp = b.p[i]-b.p[j];
				if(max_dis < temp.len())
				{
					max_dis = temp.len();
					ans1 = i;
					ans2 = j;
				}
			}
		printf("%.4f %.4f %.4f %.4f\n", b.p[ans1].x, b.p[ans1].y, b.p[ans2].x, b.p[ans2].y);
	}
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值