您的位置:首页 > 其它

Codeforces Gym - 101635K Blowing Candles [旋转卡壳]

2017-12-17 14:16 393 查看
题意:给你平面上n个点,求用最短的线段平移覆盖所有的点。

题解:既然线段能覆盖所有的点,那么可以知道是两条平行线之间包含所有的点,其实就是找凸包上的对踵点对。

对踵点对:对于多边形来说,如果两个点 p 和 q (属于 P) 在两条平行切线上, 那么他们就形成了一个对踵点对。 

易得:最近的一对平行线,一定包含多边形的一条边,于是我们可以通过旋转卡壳,求出所有对踵点对,然后求点到直线的距离的最小值,就可以得到答案。

AC代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define INF 9999999999999.9
#define PI acos(-1.0)
struct Point
{
double x, y, dis;
}pt[200005], stack[200005], p0;
int top, tot;
//计算几何距离
double Dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
//极角比较, 返回-1: p0p1在p0p2的右侧,返回0:p0,p1,p2共线
int Cmp_PolarAngel(struct Point p1, struct Point p2, struct Point pb)
{
double delta=(p1.x-pb.x)*(p2.y-pb.y)-(p2.x-pb.x)*(p1.y-pb.y);
if (delta<0.0) return 1;
else if (delta==0.0) return 0;
else return -1;
}
// 判断向量p2p3是否对p1p2构成左旋
bool Is_LeftTurn(struct Point p3, struct Point p2, struct Point p1)
{
int type=Cmp_PolarAngel(p3, p1, p2);
if (type<0) return true;
return false;
}
//先按极角排,再按距离由小到大排
int Cmp(const void*p1, const void*p2)
{
struct Point*a1=(struct Point*)p1;
struct Point*a2=(struct Point*)p2;
int type=Cmp_PolarAngel(*a1, *a2, p0);
if (type<0) return -1;
else if (type==0)
{
if (a1->dis<a2->dis) return -1;
else if (a1->dis==a2->dis) return 0;
else return 1;
}
else return 1;
}
//求凸包
void Hull(int n)
{
int i, k;
p0.x=p0.y=INF;
for (i=0;i<n;i++)
{
scanf("%lf %lf",&pt[i].x, &pt[i].y);
if (pt[i].y < p0.y)
{
p0.y=pt[i].y;
p0.x=pt[i].x;
k=i;
}
else if (pt[i].y==p0.y)
{
if (pt[i].x<p0.x)
{
p0.x=pt[i].x;
k=i;
}
}
}
pt[k]=
4000
pt[0];
pt[0]=p0;
for (i=1;i<n;i++)
pt[i].dis=Dis(pt[i].x,pt[i].y, p0.x,p0.y);
qsort(pt+1, n-1, sizeof(struct Point), Cmp);
//去掉极角相同的点
tot=1;
for (i=2;i<n;i++)
if (Cmp_PolarAngel(pt[i], pt[i-1], p0))
pt[tot++]=pt[i-1];
pt[tot++]=pt[n-1];
//求凸包
top=1;
stack[0]=pt[0];
stack[1]=pt[1];
for (i=2;i<tot;i++)
{
while (top>=1 && Is_LeftTurn(pt[i], stack[top], stack[top-1])==false)
top--;
stack[++top]=pt[i];
}
}
//计算叉积
double CrossProduct(struct Point p1, struct Point p2, struct Point p3)
{
return (p1.x-p3.x)*(p2.y-p3.y)-(p2.x-p3.x)*(p1.y-p3.y);
}
//卡壳旋转,求出凸多边形所有对踵点
double hl(double a,double b,double c)
{
double p=(a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
double dist(Point a,Point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void Rotate(struct Point*ch, int n)
{
int i, p=1;
double t1, t2, ans=INF, dif;
ch
=ch[0];
for (i=0;i<n;i++)
{
//如果下一个点与当前边构成的三角形的面积更大,则说明此时不构成对踵点
while (fabs(CrossProduct(ch[i],ch[i+1],ch[p+1])) > fabs(CrossProduct(ch[i],ch[i+1],ch[p])))
p=(p+1)%n;
dif=fabs(CrossProduct(ch[i],ch[i+1],ch[p+1])) - fabs(CrossProduct(ch[i],ch[i+1],ch[p]));
//如果当前点和下一个点分别构成的三角形面积相等,则说明两条边即为平行线,对角线两端都可能是对踵点
t1=hl(dist(ch[i],ch[i+1]),dist(ch[i+1],ch[p]),dist(ch[p],ch[i]))*2.0/dist(ch[i],ch[i+1]);
//printf(">>%lf\n",dist(ch[i],ch[i+1]));
if (t1<ans)ans=t1;
}
printf("%.15lf\n",ans);
}
int main (void)
{
int n;
scanf("%d%*d",&n);
Hull(n);
Rotate(stack, top+1);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: