概述
题意:
给定n个点,求凸边形宽的最小值
凸边形的宽指的是如同用游标卡尺卡着多边形的宽度
图中蓝线即宽;
引入概念:旋转卡壳
- 名称来源于算法的过程就像用游标卡尺卡着凸包旋转一周一样。
最小宽度算法思想:
- 1.计算多边形 y 方向上的端点。 我们称之为 ymin 和 ymax
2.通过 ymin 和 ymax 构造两条水平切线。 由于他们已经是一对对踵点, 计算他们之间的距离并维护为一个当前最大值。
3.同时旋转两条线直到其中一条与多边形的一条边重合。
4.一个新的对踵点对此时产生。 计算新的距离, 并和当前值比较, 并更新。
5.重复步骤3和步骤4的过程直到 再次产生对踵点对 (ymin,ymax) 。
算法的一点优化:
- (1)如果这样直接去实现不太好实现,我们可以转化一下,就是这一对平行线上在旋转的过程中我们可以让其中一条与凸包的一条边重合,此时另一条线的顶点是距这条边最远的点。我们可以依次枚举每条边求得距离这条最远的顶点,然后计算这个顶点到边两个端点的距离并取较大的一个,枚举完成时得到的最大距离即为直径长度。但是这样算的话因为枚举了所有边,每次又要枚举所有顶点,因此时间复杂度为O(n^2)。
(2)但是可以想到,当枚举的边逆时针旋转时,最远点也是跟着逆时针变化,这样我们可以不用每次枚举所有的顶点,直接从上次的最远点开始继续计算即可,此时复杂度为O(n)。
题解:
- 先计算凸包,在利用旋转卡壳的思想求解
代码:
#include <iostream>
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
double EPS=1e-10;
double add(double a,double b)
{
if(abs(a+b)<EPS*(abs(a)+abs(b))) return 0;
return a+b;
}
struct point{
double x,y;
point(){}
point(double x,double y):x(x),y(y){
}
point operator +(point p)
{
return point(add(x,p.x),add(y,p.y));
}
point operator -(point p)
{
return point(add(x,-p.x),add(y,-p.y));
}
point operator *(double d)
{
return point(x*d,y*d);
}
double dot(point p)
{
return add(x*p.x,y*p.y);
}
double det(point p)
{
return add(x*p.y,-y*p.x);
}
};
double cross_product(point x,point y,point z)
{
return (z-x).det(z-y);
}
double dis(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
/****************/
/**
旋转卡壳
**/
/****************/
double rotating_caliper(vector<point> v)
{
double min_dis = INF;
int n = v.size();
v.push_back(v[0]);
for (int i = 0; i < n; ++i)
{
double res=0;
for(int j=0;j<n;j++)
{
res=max(res,cross_product(v[i],v[i+1],v[j])/dis(v[i],v[i+1]));
}
min_dis = min(min_dis,res);
}
return min_dis;
/*****************************************************************************************************/
/***
优化的思路 但是莫名tle了
***/
/**
int j = 2;
***/
/**
for (int i = 0; i < n; ++i)
***/
/**
{
***/
/**
while (cross_product(v[i], v[i + 1], v[j]) < cross_product(v[i], v[i + 1], v[j + 1]))
***/
/**
{
***/
/**
j = (j + 1) % n;
***/
/**
}
***/
/**
min_dis = min(min_dis,cross_product(v[i],v[i+1],v[j])/dis(v[i],v[i+1]) );
***/
/**
}
***/
/*****************************************************************************************************/
}
/****************/
/**
计算凸包
**/
/****************/
bool cmp(const point& p,const point& q)
{
if(p.x!=q.x) return p.x<q.x;
return p.y<q.y;
}
vector<point> convex_hull(point *ps,int n)
{
sort(ps,ps+n,cmp);
int k=0;
vector<point> qs(n*2);
for(int i=0;i<n;i++)
{
while(k>1&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;
qs[k++]=ps[i];
}
for(int i=n-2,t=k;i>=0;i--)
{
while(k>t&& (qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;
qs[k++]=ps[i];
}
qs.resize(k-1);
return qs;
}
point ps[105];
int main()
{
int n;
cin>>n;
int cnt=0;
for(int i=0;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ps[cnt++]=point(a,b);
}
vector<point> qs=convex_hull(ps,n);
double res=rotating_caliper(qs);
printf("%.10fn",res);
return 0;
}
望大佬不吝赐教……
文章部分内容来源:
http://blog.csdn.net/wang_heng199/article/details/74477738
http://blog.csdn.net/u012328159/article/details/50809014
最后
以上就是时尚导师为你收集整理的Gym - 101606B 旋转卡壳的全部内容,希望文章能够帮你解决Gym - 101606B 旋转卡壳所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复