凸包求法 POJ 1113 Wall 凸包
2017-03-26 22:43
369 查看
题目大意:
有个愚蠢的皇帝要你造墙将城堡围起来,城堡的顶点有N个,墙必须离城堡的边至少L单位远,并且墙的总长度尽量小。求此长度?
因为墙的长度要尽量小,所以墙不能凹进去,一定是一个凸多边形。如图,最终的墙类似虚线部分,由凸包的周长和一个半径L的圆构成,于是求出凸包就搞定了。因为走一圈,经过拐点时,所形成的扇形的内角和是360度,总会形成一个完整的圆。故结果等于,凸包周长+一个完整的圆周长。
值得注意的是,这种题目精度往往比较坑,四舍五入!=(int)类型强制转换。
下面的程序套用的模板,比较全面,所以保留了lf(本题中int就行)。
求凸包主要思路就是先排序(y为第一关键字,x为第二关键字,不排序的话有可能会从最低点出发沿着左半凸走,每一次都是非法路径,就把点删完了,找不到凸包),找到左下角与右上角的两个点将整个凸包分为两部分(因为找右半凸的时候有可能找到左半凸上边的点,因为不合法就会删掉这个点,那么之后我们找左半凸的时候就会受影响)。用叉乘判断是否合法,叉乘结果为负时说明新的边在旧的边右侧,而因为我们是逆时针找凸包的(如果要顺时针找,那么叉乘为正不合法),凸包又是一个凸多边形,所以整个图形应当在任一凸包上边的左侧,也就是说,沿着凸包走只能左转。
通过这种方法就求得了凸包。
这里给大家提供另一个方法,就是排序的时候按照极角排序,其实就是按照每个点与最低点连线的斜率从小到大排序(为什么用最低点呢?其实按理说找任意的点都是可以的,只不过我们为了不处理负斜率就选取最低点),这样的话就不用找最高点了,从最低点出发绕一圈即可。可以用反证法证明在寻找右半凸时不会找到左半凸,假设先找到了左半凸,那么就说明有至少一个右半凸上的点(连线)的斜率大于这个左半凸上的点(连线)的斜率,那么就是说从这个右半凸上的点走到这个左半凸上的点会向右转,不满足在凸包上的点的性质——只向右转。所以说得证。
下面附上代码:
并不是笔者的代码,懒得写了,所以说凑合着看看吧,主要是排序那一块。
有个愚蠢的皇帝要你造墙将城堡围起来,城堡的顶点有N个,墙必须离城堡的边至少L单位远,并且墙的总长度尽量小。求此长度?
因为墙的长度要尽量小,所以墙不能凹进去,一定是一个凸多边形。如图,最终的墙类似虚线部分,由凸包的周长和一个半径L的圆构成,于是求出凸包就搞定了。因为走一圈,经过拐点时,所形成的扇形的内角和是360度,总会形成一个完整的圆。故结果等于,凸包周长+一个完整的圆周长。
值得注意的是,这种题目精度往往比较坑,四舍五入!=(int)类型强制转换。
下面的程序套用的模板,比较全面,所以保留了lf(本题中int就行)。
求凸包主要思路就是先排序(y为第一关键字,x为第二关键字,不排序的话有可能会从最低点出发沿着左半凸走,每一次都是非法路径,就把点删完了,找不到凸包),找到左下角与右上角的两个点将整个凸包分为两部分(因为找右半凸的时候有可能找到左半凸上边的点,因为不合法就会删掉这个点,那么之后我们找左半凸的时候就会受影响)。用叉乘判断是否合法,叉乘结果为负时说明新的边在旧的边右侧,而因为我们是逆时针找凸包的(如果要顺时针找,那么叉乘为正不合法),凸包又是一个凸多边形,所以整个图形应当在任一凸包上边的左侧,也就是说,沿着凸包走只能左转。
通过这种方法就求得了凸包。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <cmath> # include <algorithm> using namespace std; #define rep(i,l,r) for(int i=l;i<=r;i++) #define drep(i,r,l) for(int i=r;i>=l;i--) #define min(a,b) (a<b?a:b) #define max(a,b) (a>b?a:b) #define LL long long #define sqr(x) ((x)*(x)) #define suf(x) (x==n?1:x+1) #define pi 3.1415926535897932384626433832795 const int N = 1010; int n, l; struct point{ double x , y; point(){} point(double x1 , double y1) {x = x1; y = y1;} inline double operator%(const point &a)const{//叉乘 return x*a.y - y*a.x; } inline double operator*(const point &a)const{//点积 return x*a.x + y*a.y; } inline point operator+(const point &a)const{ return point(x + a.x , y + a.y); } inline point operator-(const point &a)const{ return point(x - a.x , y - a.y); } inline point operator*(double k)const{ return point(x*k , y*k); } inline point operator/(double k)const{ return point(x/k , y/k); } }a , b ; inline bool cmp(const point &a , const point &b){ //以y为第一关键字,x为第二关键字排序,保证从下到上 return a.y < b.y || a.y == b.y && a.x < b.x; } inline double Cross(point &a , point &b , point &c){ return (b-a) % (c-a);//向量ab叉乘向量ac } inline double dis(point &a , point &b){//两点之间距离 return sqrt(sqr(a.x-b.x) + sqr(a.y-b.y)); } inline void Tubao(){ sort(a+1, a+n+1, cmp); int m = 0; rep(i,1,n){ while (m > 1 && Cross(b[m-1] , b[m] , a[i]) <= 0) m--; //只有一条边时一定合法,叉乘<0时不合法,把中间所有不合法的点都去掉,加入这个新的点 b[++m] = a[i];//合法就保存 } int j = m; drep(i,n,1){ while (m > j && Cross(b[m-1] , b[m] , a[i]) <= 0) m--; b[++m] = a[i]; } n = m; rep(i,1,n) a[i] = b[i];//返回到a(可无) } inline void Init(){ scanf("%d%d", &n, &l); rep(i,1,n) scanf("%lf%lf" , &a[i].x , &a[i].y); Tubao(); } inline void work(){ double ans = 0; for(int i=1; i<n; i++){ ans += dis(a[i], a[i+1]);//凸包边长 } ans += (2 * l) * pi;//圆周长(四舍五入->强转int) printf("%d", (int)(ans + 0.5)); /*for(int i=1; i<=n; i++){ printf("\n%lf %lf", a[i].x, a[i].y); }*/ } int main(){ //while(scanf("%d%d", &n, &l) != EOF){ Init(); work(); //} return 0; }
这里给大家提供另一个方法,就是排序的时候按照极角排序,其实就是按照每个点与最低点连线的斜率从小到大排序(为什么用最低点呢?其实按理说找任意的点都是可以的,只不过我们为了不处理负斜率就选取最低点),这样的话就不用找最高点了,从最低点出发绕一圈即可。可以用反证法证明在寻找右半凸时不会找到左半凸,假设先找到了左半凸,那么就说明有至少一个右半凸上的点(连线)的斜率大于这个左半凸上的点(连线)的斜率,那么就是说从这个右半凸上的点走到这个左半凸上的点会向右转,不满足在凸包上的点的性质——只向右转。所以说得证。
下面附上代码:
#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)); }
并不是笔者的代码,懒得写了,所以说凑合着看看吧,主要是排序那一块。
相关文章推荐
- POJ 1113 Wall 凸包
- POJ 1113-Wall(凸包-Graham算法)
- nyoj 226 && hdu HDU 1348 && poj 1113 wall [凸包周长]
- POJ 1113 Wall (凸包求周长)
- poj 1113 Wall----凸包 (给自己的一句话: 没AC,没理由!)
- poj_1113 Wall(凸包)
- poj 1113 Wall凸包
- poj-1113-Wall(凸包问题)
- [poj 1113]Wall[凸包]
- POJ 1113 Wall (凸包周长)
- POJ训练计划1113_Wall(几何/凸包)
- poj 1113 wall 简单的凸包
- poj 1113 Wall (凸包)
- POJ 1113 Wall 求凸包的两种方法
- poj 1113:Wall(计算几何,求凸包周长)
- POJ 1113 Wall (凸包)
- 简单几何(凸包) POJ 1113 Wall
- 【POJ】1113 Wall(凸包)
- POJ1113:Wall(凸包)
- 【解题报告】 POJ 1113 Wall -- 求凸包周长