POJ1113 计算几何雏形:凸包!
2017-03-26 22:12
323 查看
今天pty大神给我们讲了计算几何的基本,能几乎听懂的感觉还是不错的,但不能调出来一道模板题的话,总不能感觉掌握了。嗯,就找了这道堪称计算几何的模板来做了一下,发现,嗯,是我小看了。。。一上午和一下午啊。。的确是有写点东西的必要了。
老样子首先说明一下题意,那个,给你一座城堡,让你在城堡周围修一堵围墙,距离城堡的距离为k,嗯。乍一看有点古怪,其实好好看一下图就知道了,就是城堡的周长加上了一个圆的周长,因为围着城堡绕一圈正好赚了360度对吧,那不就正好一个圆吗,,那就只用考虑城堡的周长就行了嗯。
城堡是通过拐角的点给出的,换言之就是要通过给出的点求出城堡的周长,就是说包围着这些点的“凸包”,求凸包咯!
具体来说怎么做呐,就是GRAHAM求法,即排序+查找;
首先是排序,把所有的点排一遍序,点排序的依据是极角(马上解释),首先找到所有点中左下地方的那个点,就是纵坐标最低,如果纵坐标一样就比较横坐标,找较小的那一个,可以证明这样的一个点一定在凸包的“壳”上,那么就把这么个点记为0号点,从这个点开始找凸壳吧!,找点的代码:
point p0; scanf("%d%d",&a[0].x,&a[0].y); p0.x=a[0].x,p0.y=a[0].y; for(int i=1;i<n;i++) { scanf("%d%d",&a[i].x,&a[i].y); if((p0.y>a[i].y)||(p0.y==a[i].y)&&(p0.x>a[i].x)) { p0.x=a[i].x,p0.y=a[i].y; pos=i; } } a[pos]=a[0]; a[0]=p0;
这里的p0只是个临时变量,作用是找到最“矮”的点并把它存到a0里,嗯,就是这样。
然后就是我们的排序了,用快排方便加快捷,但cmp函数要自己打:
bool cmp(const point &aa,const point &b) { int ans=cj(a[0],aa,b); if(ans>0) return true; else if(ans==0&&dis(a[0],aa)<dis(a[0],b)) return true; else return false; }
来解释一下,这里呢,是极角排序,什么是极角呐,就是以我们选定的a0为源点,建立平面直角坐标系,每一个点与源点的连线,与y轴的夹角大小排序,夹角越大,排序越小,这样就能保证依次遍历图时,能够从右边绕到左边来,嗯。但是呐,a0的值最好不要参与排序,因为其他都是按极角排序的,a0是按位置确定的,这样能避免a0的重复计算,嗯。
然后就是关键的一步了,找凸壳!要分一下类:
如果只有一个点的话,凸壳也无所谓了
有2个点的话,凸壳就只有一条边而已,入栈就行了。
有2个以上的边的话,先依次入栈,如果向左转(叉积>0)的话,直接入栈就行了,否则(叉积<0)向右转时就不是凸包了,就把栈弹出,弹到栈首点能够向左转到达时,就把枚举的点入栈!代码:
if(n>2) { stack[0]=a[0],stack[1]=a[1]; for(int i=2;i<n;i++) { while(top>0&&cj(stack[top-1],stack[top],a[i])<=0) top--; stack[++top]=a[i]; } }
如此,待全部的点都遍历完了后,保存在栈里的点就是凸壳了,把他们的距离依次加起来即可,代码:
for(int i=0;i<=top;i++) sum+=dis(stack[i],stack[(i+1)%(top+1)]);
呐,完整代码:
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
const double PI=acos(-1.0);
struct point
{
int x,y;
}a[100010],stack[100010];
double sum=0;
int n,k;
double dis(point a,point b)
{
return sqrt((double)(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int cj(point p0,point p1,point p2)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
bool cmp(const point &aa,const point &b) { int ans=cj(a[0],aa,b); if(ans>0) return true; else if(ans==0&&dis(a[0],aa)<dis(a[0],b)) return true; else return false; }
int main()
{
int pos=0;
scanf("%d%d",&n,&k);
point p0;
scanf("%d%d",&a[0].x,&a[0].y);
p0.x=a[0].x,p0.y=a[0].y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
if((p0.y>a[i].y)||(p0.y==a[i].y)&&(p0.x>a[i].x))
{
p0.x=a[i].x,p0.y=a[i].y;
pos=i;
}
}
a[pos]=a[0];
a[0]=p0;
int top=1;
sort(a+1,a+n,cmp);
if(n == 1)
{
top=0;stack[0]=a[0];
}
if(n == 2)
{
top=1;
stack[0]=a[0];
stack[1]=a[1];
}
if(n>2) { stack[0]=a[0],stack[1]=a[1]; for(int i=2;i<n;i++) { while(top>0&&cj(stack[top-1],stack[top],a[i])<=0) top--; stack[++top]=a[i]; } }
for(int i=0;i<=top;i++)
sum+=dis(stack[i],stack[(i+1)%(top+1)]);
printf("%d",(int)(sum+2*PI*k+0.5));
}
如何,不长吧?关于叉积判断方向,就请自己了解下吧,我的算叉积的函数也是直接套用的叉积的公式,证明和推论请参考百度百科!嗯嗯。
相关文章推荐
- [POJ1113]Wall(计算几何-凸包)
- POJ1113 计算几何--整形凸包模板周长
- 计算几何:凸包(圈水池)
- HDU 1392 (计算几何 凸包)
- 【BZOJ】【P1964】【hull 三维凸包】【题解】【计算几何】
- 【Codeforces Round 335 (Div 2)E】【计算几何-凸包 线性规划 三分凸包上最优点】Freelancer's Dreams 二维属性 充最少的钱变得满足要求 [计算几何-凸包模
- 【计算几何】poj1113 Wall
- [计算几何-凸包]pku1113-Wall
- 三维计算几何模板--表面三角形个数 表面多边形个数 三维凸包 表面积 凸包重心 点到面的距离
- 计算几何凸包详解
- pku 3348 计算几何 求凸包面积
- 计算几何-凸包
- bzoj1670: [Usaco2006 Oct]Building the Moat护城河的挖掘 计算几何 凸包
- 1214: [视频]【计算几何】凸包
- 2013 多校第七场 hdu 4667 Building Fence(计算几何、凸包)
- hdu 1616 计算几何 凸包
- 计算几何模板——凸包
- 二维计算几何模板--多边形/凸包
- UVA 10652 (计算几何 凸包)
- HDU 4273(计算几何+凸包重心)