poj3525 Most Distant Point from the Sea(半平面交+二分)

qwq我是个睿智

首先,题目要求的是给定的一个凸多边形中距离边缘最远的点的距离是多少

qwq由于没有求最远的关键点之类的,所以我们并不好通过一些手段来直接求出来距离是多少。

那么就想到了二分距离

如果我们将原图的每条有向直线,逆时针构造,然后将左边视为他代表的半平面的话,那么问题就能够转化成,我们将原图中每一条边向内部(或者是直接说成沿着垂直方向)向内部平移 m i d mid 的距离后,判断是否存在半平面交。
如果存在,说明当前答案是合法的, 如果不存在就是不合法的。

那么qwq
貌似问题就成了如何将直线平移呢?
qwq大概是孙神教给我的一个方法。

既然是我们是两点式子来表示一个直线,那我们可以直接推出来一个移动向量,也就是横纵坐标分别需要变化多少。

在这里插入图片描述

对于这个图来说,假设我们要求的是这个情况下的移动情况,我们可以对 m i d mid 这个东西进行正交分解,然后,我们可以通过 ( m i d sin θ , m i d cos θ ) (-mid*\sin \theta,mid*\cos \theta) 来表示
那么该如何求这个东西呢。

我们发现我们可以利用这个有向直线所在的这个三角形来推出来,那么不难发现 sin θ = z . y z . x 2 + z . y 2 \sin\theta = \frac{z.y}{\sqrt {z.x^2 + z.y^2}}

c o s cos 也是同理。

这里有一个需要注意的细节是,由于平移并不会改变极角,所以我们一开始直接按照极角排好序,直接平移,就能将复杂度做到 O ( n l o g n ) O(nlogn)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e5+1e2;
const double eps = 1e-7;
const double pai = acos(-1);
const double inf = 10001;
int dcmp(double x)
{
	if (x>eps) return 1;
	if (x<-eps) return -1;
	return 0;
}
struct Point
{
  	double x,y;
}; 
Point operator + (Point x,Point y)
{
	return (Point){x.x+y.x,x.y+y.y};
}
Point operator - (Point x,Point y)
{
	return (Point){x.x-y.x,x.y-y.y};
}
Point operator * (Point x,double y)
{
	return (Point){x.x*y,x.y*y};
}
Point operator / (Point x,double b)
{
	return (Point){x.x/b,x.y/b};
}
struct Line
{
	Point x,y;
	double ang;
};
Line count(Point x,Point y)
{
    return (Line) {x,y,atan2(y.y-x.y,y.x-x.x)};
}
double mochang(Point x)
{
	return sqrt(x.x*x.x+x.y*x.y);
}
double diancheng(Point a,Point b)
{
	return a.x*b.x+a.y*b.y;
}
double chacheng(Point a,Point b)
{
	return a.x*b.y-a.y*b.x;
}
bool pingxing(Line a,Line b)
{
	return dcmp(chacheng(a.y-a.x,b.y-b.x))==0;
}
bool isright(Line a,Point b)
{
	return dcmp(chacheng(a.y-a.x,b-a.x))==-1;
}
Point jiaodian(Line a,Line b)
{
	return a.x+(a.y-a.x)*(chacheng(b.y-b.x,a.x-b.x)/chacheng(a.y-a.x,b.y-b.x));
}
bool operator < (Line a,Line b)
{
   double now = a.ang-b.ang;
   if (dcmp(now)!=0)
   {
   	  if (dcmp(now)==-1) return 1;
   	  else return 0;
   }
   else
   {
   	  double tmp = chacheng(a.y-a.x,b.y-a.x);
   	  if (dcmp(tmp)==-1) return 1;
   	  else return 0;
   }
}
Point a[maxn];
int n,m;
Line l[maxn];
Line qx[maxn];
Point qd[maxn];
Line ymh[maxn];
int num;
bool solve(int n)
{
	int head=0,tail=1;
	//sort(l+1,l+1+n);
	qx[++head]=l[1];
	for (int i=2;i<=n;i++)
	{
		if (dcmp(l[i].ang-l[i-1].ang)==0) continue;
		if (head<tail && (pingxing(qx[head],qx[head+1]) || pingxing(qx[tail],qx[tail-1]))) return false;
		while(head<tail && isright(l[i],qd[tail-1])) tail--;
		while(head<tail && isright(l[i],qd[head])) head++;
		qx[++tail]=l[i];
		if (head<tail) qd[tail-1] = jiaodian(qx[tail],qx[tail-1]);
		//cout<<i;
		//printf(" %.6lf\n",l[i].ang);
	}
//	cout<<"*****"<<endl;
    while (head<tail && isright(qx[head],qd[tail-1])) tail--;
	while (head<tail && isright(qx[tail],qd[head])) head++;
	
	if (tail-head+1<=2) return false;
	qd[tail]=jiaodian(qx[head],qx[tail]);
	return true;
}
Point getvec(Point x,Point y,double l)
{
	Point now  = y-x;
	double tmp = mochang(now);
	return (Point){(-1)*l*(now.y/tmp),l*(now.x/tmp)};
}
bool check(double mid)
{
	m=0;
	for (int i=1;i<=num;i++)
	{
		Point now = getvec(ymh[i].x,ymh[i].y,mid);
		++m;
		l[m].x=ymh[i].x+now;
		l[m].y=ymh[i].y+now;
		l[m].ang=ymh[i].ang;
	}

	return solve(m);
}
int main()
{
  while (1)
  {
  	n=read();
  	num=0;
  	if (n==0) break;
  	for (int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y); 
  	a[n+1]=a[1];
  	for (int i=1;i<=n;i++) ymh[++num]=count(a[i],a[i+1]);
	Point a = (Point) {0,0};
	Point b = (Point) {inf,0};
	Point c = (Point) {inf,inf};
	Point d = (Point) {0,inf};
	ymh[++num]=count(a,b);
	ymh[++num]=count(b,c);
	ymh[++num]=count(c,d);
	ymh[++num]=count(d,a);
  	sort(ymh+1,ymh+1+num);
  	double l=0,r=inf;
  	double ans=0;
  	while (r-l>=eps)
  	{
  		double mid =(l+r)/2;
  		if (check(mid)) l=mid,ans=mid;
  		else r=mid;
	}
	printf("%.6f\n",ans);
  }  
  return 0;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/87207097
今日推荐