⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zju_2419.cpp

📁 ACM-ICPC竞赛 计算几何 终极学习资料整理合集 包含多篇PPT
💻 CPP
字号:
/*
凸包( Convex Hull )
凸包是对平面是上的某个点集而言的,凸包是一个最小凸多边形,满足点集
中的所有点都在该凸多边形内(或在该多边形的边上)。
通常,我们采用Graham扫描法来求点集的凸包。首先,排序选出点集中最左下
角点(先取y坐标最小的点,若有多个再在其中取x坐标最小的点),设该点为p0;
然后,将其余的按以p0为中心的极角坐标逆时针排序,多于相同极角的点只保留
距离p0最远的一个,这样就可以得到一个点的序列p1,p2, p2.....,pn;接下来,
将p0, p1, p2压入栈,一次处理pi(i >= 2 && i <= n),不断让栈顶的元素出
栈,直到栈顶的下一个元素,栈顶元素,以及pi构成的折线不拐向左侧,将pi压
入栈中;最后栈中的元素即为所求的凸包的顶点序列  
*/ 
/*
腾讯公司不会以QQ聊天形式通知中奖,请谨防中奖陷阱。

  蓝点.rcu 13:23:13
  嗯~~O(n^2)的应该没有问题
  ¤海子⊙   13:23:36
  谢谢你,
  ¤海子⊙   13:23:41
  说我听听
  蓝点.rcu 13:23:51
  这样子,先弄出一条直线~~这条直线穿过两个点。
  蓝点.rcu 13:24:05
  外层循环用来枚举跳过的点数。
  蓝点.rcu 13:24:19
  (跳过的方向是顺时针或者逆时针)
  ¤海子⊙   13:24:46
  这是O(n^3)
  蓝点.rcu 13:24:56
  你等我说完。
  向内的一层循环,枚举起始点,
  蓝点.rcu 13:25:32
  最后的那一点,不要再加循环进行枚举。
  蓝点.rcu 13:25:57
  利用峰值原则和起始点进行同步调整。
  蓝点.rcu 13:26:11
  峰值原则是这样的……
  蓝点.rcu 13:26:33
  就是 rotating calipers 的一个方面。
  蓝点.rcu 13:26:41
  先简化情况。
  ¤海子⊙   13:26:42
  峰值原则是什么
  蓝点.rcu 13:28:57
  假设最大三角形有两个点相邻
  (两个点不相临的情况在上述算法中用枚举处理掉了)
  以这两个点为底边,如果对另外一点进行扫描,则三角形高的最大值只可能有一个(可能出现多次,但是出现的这几次必定相邻,否则就不是凸包了)。
  蓝点.rcu 13:29:50
  
	蓝点.rcu 13:29:57
	这种情况可能发生
	蓝点.rcu 13:30:21
	上方的三点作为顶点都可以使得面积最大
	¤海子⊙   13:30:44
	上方的三点作为顶点面积不是零吗
	¤海子⊙   13:31:02
	哦,知道了,底边
	蓝点.rcu 13:31:11
	不是,是以下面的边为底边,以上面三点任何一点为另外顶点
	蓝点.rcu 13:31:16
	
	  ¤海子⊙   13:31:31
	  这不是凸包吧
	  蓝点.rcu 13:31:35
	  这种情况不可能出现。
	  因为这不是凸包
	  蓝点.rcu 13:32:33
	  峰值原则 1:
	  如果以固定的为底边,则三角形面积最大值至少出现一次,而且如果出现多次,则必连续出现。
	  ¤海子⊙   13:32:59
	  哦,这个好理解,凸包的性质
	  ¤海子⊙   13:34:23
	  哦,两层循环就是枚举底边的两个顶点?
	  蓝点.rcu 13:34:24
	  嗯,然后,还有一点,我忘记如何证明了,底边进行步进旋转的同时,峰值出现的位置沿着底边旋转的方向单调增加
	  蓝点.rcu 13:34:41
	  两层循环其实就是枚举顶点
	  ¤海子⊙   13:34:57
	  是枚举底边的两个顶点?
	  蓝点.rcu 13:35:07
	  但是根据峰值原则的这一条,外层必须用来枚举跳点个数
	  蓝点.rcu 13:35:12
	  其实就是枚举两个顶点
	  
		蓝点.rcu 13:35:21
		但是枚举的顺序是要严格小心的
		¤海子⊙   13:35:48
		枚举跳点个数,跳点是什么
		蓝点.rcu 13:36:23
		就是三角形底边两顶点之间跳过的凸包上的点的数量
		蓝点.rcu 13:37:39
		如此呢~~就可以旋转了~~
		¤海子⊙   13:37:44
		哦,我理解一下
		蓝点.rcu 13:38:00
		就是这样的:外层控制底边,内层控制旋转。
		蓝点.rcu 13:38:20
		千万不要多加一层哈~~那样就成(n^3)了……
		¤海子⊙   13:38:49
		哦,是呀,
		蓝点.rcu 13:39:06
		我想想……应该可以继续优化……
		¤海子⊙   13:39:12
		谢谢你啊,我理解一下,
		蓝点.rcu 13:39:17
		嗯,不客气~~
		¤海子⊙   13:39:23
		是呀,有O(n)的
		蓝点.rcu 13:40:44
		那我想想哈~
		¤海子⊙   13:40:50
		
		  ¤海子⊙   13:47:04
		  外层循环枚举跳点个数,内层循环枚举起始点,然后找峰值点
		  ¤海子⊙   13:47:21
		  峰值点是有什么规律吗
		  蓝点.rcu 13:47:36
		  随着底边的旋转方向单调旋转
		  蓝点.rcu 13:47:46
		  所以就可以降为(N^2)
		  蓝点.rcu 13:47:50
		  嗯我知道O(n)大概是怎么回事了……
		  ¤海子⊙   13:47:58
		  哦,这么快
		  蓝点.rcu 13:48:03
		  等我想清楚哈……
		  ¤海子⊙   13:48:11
		  哦,好,我把这个想想
		  蓝点.rcu 13:56:49
		  嗯,知道了……还是峰值原则~~
		  蓝点.rcu 13:57:07
		  首先三点重合。
		  蓝点.rcu 13:57:30
		  然后呢,推动一下,推成连续的三点。
		  蓝点.rcu 13:57:39
		  初始化完毕。
		  蓝点.rcu 13:58:18
		  固定,尾点推动无论如何不能超过首点的起始位置。
		  蓝点.rcu 13:59:38
		  之后呢,就可以玩了,推动尾点,使得面积达到峰值,然后,推动中点,使得面积达到峰值,继而推动首点,使得面积达到峰值、
		  蓝点.rcu 13:59:49
		  然后推动尾点……如此下去……
		  蓝点.rcu 14:00:04
		  直到尾点达到首点的初始位置。
		  蓝点.rcu 14:01:31
		  三个点,都只能转一圈,而且,每一次顺序推进操作,至少使得尾点向前推动一次,因此,最多执行n次,在每一次中,单调推动三个点,常数复杂度。
		  蓝点.rcu 14:01:42
		  于是整个算法复杂度为O(n)……
		  蓝点.rcu 14:01:46
		  我懒得证明了……
		  蓝点.rcu 14:01:56
		  *_*
		  在忙一个工程……
		  ¤海子⊙   14:07:57
		  不好意思,我刚才出去了一下
		  蓝点.rcu 14:07:58
		  您好,我现在有事不在,一会儿再和您联系
		  ¤海子⊙   14:08:29
		  哦,你在忙一个工程啊,好,谢谢你啊
		  蓝点.rcu 14:08:30
		  您好,我现在有事不在,一会儿再和您联系
		  蓝点.rcu 14:14:22
		  嗯~~不客气~~
		  你是黄冈人哈?
		  ¤海子⊙   14:14:29
		  是呀,
		  ¤海子⊙   14:14:35
		  你也是?
		  蓝点.rcu 14:15:08
		  我是北方人啦~不过~~
		  呵呵~~我女朋友是黄冈中学的。
		  ¤海子⊙   14:15:42
		  这么强,黄冈高中?
		  ¤海子⊙   14:15:48
		  我是垃圾学校出来的
		  蓝点.rcu 14:17:08
		  嗯,她高中是黄冈中学的~~
		  
			呵呵~~不管啦~~
			现在过得好才是王道 ~~
			¤海子⊙   14:17:15
			呵呵,
			¤海子⊙   14:17:38
			你忙吧,以后多指教我啊,谢谢你,你真是个好人
			
			  
*/
//Rotating Calipers algorithm

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MaxNode 50005

int stack[MaxNode];
int top;
double max;

typedef struct TPoint
{
    int x;
    int y;
}TPoint;
TPoint point[MaxNode];

void swap(TPoint point[], int i, int j)
{
    TPoint tmp;
    tmp = point[i];
    point[i] = point[j];
    point[j] = tmp;
}

double multi(TPoint p1, TPoint p2, TPoint p0)
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

double distance(TPoint p1, TPoint p2)
{
	return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
}

int cmp(const void *a, const void *b)
{
    TPoint *c = (TPoint *)a;
    TPoint *d = (TPoint *)b;
    double k = multi(*c, *d, point[0]);
    if(k< 0) return 1;
    else if(k == 0 && distance(*c, point[0]) >= distance(*d, point[0])) return 1;
    else return -1;   
}

void grahamScan(int n)
{ 
    //Graham扫描求凸包
    int i, u; 
    
    //将最左下的点调整到p[0]的位置
    u = 0;
    for(i = 1;i <= n - 1;i++){
        if((point[i].y < point[u].y) || (point[i].y == point[u].y && point[i].x  < point[u].x))
			u = i;      
    } 
    swap(point, 0, u);
    
    //将平p[1]到p[n - 1]按按极角排序,可采用快速排序
    qsort(point + 1, n - 1, sizeof(point[0]), cmp);
    
    for(i = 0;i <= 2;i++) stack[i] = i;
    top = 2;
    for(i = 3;i <= n - 1;i++){
        while(multi(point[i], point[stack[top]], point[stack[top - 1]]) >= 0){
			top--;
			if(top == 0) break;
        }
        top++;
        stack[top] = i;
    }
}

int main()
{    
    double triangleArea(int i, int j, int k);
    void PloygonTriangle();
    int i, n;
    while(scanf("%d", &n) && n != -1){
        for(i = 0;i < n;i++)
			scanf("%d%d", &point[i].x, &point[i].y);
        if(n <= 2){
			printf("0.00\n");
            continue;      
        }
        if(n == 3){
            printf("%.2lf\n", triangleArea(0, 1, 2));
            continue; 
        }
        grahamScan(n);
        //r(i = 0;i <= top;i++)
        //intf("<%d,%d>\n", point[stack[i]].x, point[stack[i]].y);
        PloygonTriangle();	
        printf("%.2lf\n", max);    
    }
    return 0;
}

void PloygonTriangle()
{
    double triangleArea(int i, int j, int k);
    int i, j, k, u, tag;
    double area;
    i = 0;
    j = 1;
    k = 2;
    max = -1;
	tag = 1;
    while(tag){
		u = k;
		tag = 0;
		while(u <= top){
			area = triangleArea(stack[i], stack[j], stack[u]);
			if(area > max){
			    tag = 1;
				max = area;
			    k = u;
			    break;
			}
			u++;
		}
		u = j;
		while(u < k){
			area = triangleArea(stack[i], stack[u], stack[k]);
			if(area > max){
                tag = 1;
				j = u;
				max = area;
				break;
			} 
            u++;  
		}
		u = i;
		while(u < j){
			area = triangleArea(stack[u], stack[j], stack[k]);
			if(area > max){
                tag = 1;
				i = u;
				max = area;
				break;
			}
			u++;
		}
	}	
}

double triangleArea(int i, int j, int k)
{
    //已知三角形三个顶点的坐标,求三角形的面积 
    double l = fabs(point[i].x * point[j].y + point[j].x * point[k].y 
		+ point[k].x * point[i].y - point[j].x * point[i].y 
		- point[k].x * point[j].y - point[i].x * point[k].y) / 2;  
    return l;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -