计算几何 graham 最大凸包

本文介绍了如何使用Graham扫描算法解决计算几何中的最大凸包问题。在极角排序阶段,通过调整排序规则以确保近的点在前,避免使用unique函数,从而实现严格凸包的求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11326&courseid=117

凸包的严格凸多边形
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB
Total submit users: 39, Accepted users: 35
Problem 11326 : No special judgement
Problem description
  凸包(convex hull),对于给定集合X,所有包含X的凸集的交集称为X的凸包,记作con(X)。
对于ACMer来说,这么严格复杂的定义可能是没有必要的。我们只要知道平面有限点集的凸包是一个凸多边形就行了。
现在的问题是给定一个平面点集,求出其“严格凸多边形”的凸包。“严格”的意思是凸多边形的边上没有任意三点共线。
Input
  输入有多个案例。每个案例的第一行是一个整数n,n≤100。随后n行,每一行有2个整数,表示点的x、y坐标,0≤x、y≤2147483647。一个单独的0表示输入结束。没有任何点的坐标是完全一样的。
Output
  对每一个案例,首先输出一行为其端点的个数,然后按逆时针输出其凸包的顶点的坐标。输出起点是所有顶点的最下最左点(首先是最下,如果有多个点同样处于最下,则取最靠左的)。每个顶点输出一行,中间用空格隔开。
Sample Input
3
0 0
100 100
100 0
0
Sample Output
3
0 0
100 0
100 100

//严格凸包
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long llt;

#define SIZE 101

struct point_t{
	llt x,y;
}P[101];

//叉积,OA×OB
llt cross(point_t const&O,point_t const&A,point_t const&B){
    llt xoa = A.x - O.x;
	llt yoa = A.y - O.y;
	llt xob = B.x - O.x;
	llt yob = B.y - O.y;
	return xoa * yob - xob * yoa;
}

//A如果比B更靠下更靠左返回真
bool isLowLeft(point_t const&A,point_t const&B){
	return A.y < B.y || ( A.y == B.y && A.x < B.x );
}

//按照对于pO的极角排序,极角相等的距离远的排在前面,因为后面要做一个unique
point_t* pO;
bool comp4Graham(point_t const&A,point_t const&B){
    llt t = cross(*pO,A,B);
	if ( t ) return t > 0LL;
	
	llt a1 = A.x > pO->x ? A.x - pO->x : pO->x - A.x;
	llt a2 = B.x > pO->x ? B.x - pO->x : pO->x - B.x;
    if ( a1 != a2 ) return a1 > a2;

	a1 = A.y > pO->y ? A.y - pO->y : pO->y - A.y;
	a2 = B.y > pO->y ? B.y - pO->y : pO->y - B.y;
	return a1 > a2;
}

//相对于pO是否极角相等
bool isEqPolar(point_t const&A,point_t const&B){
    return 0LL == cross(*pO,A,B);
}

//Graham求凸包,结果当中没有共线点,起点总是最下最左点
int Graham(point_t P[],int n){
    if ( 1 == n ) return 1;

	//寻找最下最左点
	point_t *p = min_element(P,P+n,isLowLeft);

	//交换
	swap(*p,P[0]);

	if ( 2 == n ) return 2;

	//按极角排序,极角相等,距离近的排在前面
	pO = P;
	sort(P+1,P+n,comp4Graham);

	//将相对于pO的共线点均剔除,只保留最后一个
	p = unique(P+1,P+n,isEqPolar);
	n = p - P;

	//真正的Graham循环
	int top = 2;
	for(int i=2;i<n;++i){
		while( top > 1 && cross(P[top-2],P[top-1],P[i]) <= 0LL )
			--top;
		P[top++] = P[i];
	}
	return top;
}
int main(){
	int n;
	while( scanf("%d",&n) && n ){
        for(int i=0;i<n;++i)scanf("%I64d%I64d",&P[i].x,&P[i].y);
		n = Graham(P,n);

		printf("%d\n",n);
		for(int i=0;i<n;++i)printf("%I64d %I64d\n",P[i].x,P[i].y);
	}
	return 0;


http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11327&courseid=117

凸包的不严格凸多边形
Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB
Total submit users: 19, Accepted users: 15
Problem 11327 : No special judgement
Problem description
  凸包(convex hull),对于给定集合X,所有包含X的凸集的交集称为X的凸包,记作con(X)。
对于ACMer来说,这么严格复杂的定义可能是没有必要的。我们只要知道平面有限点集的凸包是一个凸多边形就行了。
现在的问题是给定一个平面点集,求出其“不严格”凸多边形的凸包。“不严格”的意思是指将凸包边界上所有的点都看作是多边形的端点。
Input
  输入有多个案例。每个案例的第一行是一个整数n,n≤100。随后n行,每一行有2个整数,表示点的x、y坐标,0≤x、y≤2147483647。一个单独的0表示输入结束。
Output
  对每一个案例,首先输出一行为多边形端点的个数,然后按逆时针输出其凸包的顶点的坐标。输出起点是所有顶点的最下最左点(首先是最下,如果有多个点同样处于最下,则取最靠左的)。每个顶点输出一行,中间用空格隔开。
Sample Input
4
0 0
100 100
100 0
50 0
0
Sample Output
4
0 0
50 0
100 0
100 100


严格凸包是在极角排序的时候提前做了处理,所以我们只要把处理取反即可, 代码上只要把极角相等的时候改成近的放前面,去掉unique函数。

typedef long long llt;
struct point_t{
	llt x,y;
}P[101];

llt cross(point_t const&O,point_t const&A,point_t const&B){
    llt xoa = A.x - O.x;
	llt yoa = A.y - O.y;
	llt xob = B.x - O.x;
	llt yob = B.y - O.y;
	return xoa * yob - xob * yoa;
}

bool isLowLeft(point_t const&A,point_t const&B){
	return A.y < B.y || ( A.y == B.y && A.x < B.x );
}

point_t* pO;
bool comp4Graham(point_t const&A,point_t const&B){
    llt t = cross(*pO,A,B);
	if ( t ) return t > 0LL;

	llt a1 = A.x > pO->x ? A.x - pO->x : pO->x - A.x;
	llt a2 = B.x > pO->x ? B.x - pO->x : pO->x - B.x;
    if ( a1 != a2 ) return a1 < a2; //把这个变成近的放前面

	a1 = A.y > pO->y ? A.y - pO->y : pO->y - A.y;
	a2 = B.y > pO->y ? B.y - pO->y : pO->y - B.y;
	return a1 > a2;
}

bool isEqPolar(point_t const&A,point_t const&B){
    return 0LL == cross(*pO,A,B);
}

int Graham(int n){
    if ( 1 == n ) return 1;

	point_t *p = min_element(P,P+n,isLowLeft);

	swap(*p,P[0]);

	if ( 2 == n ) return 2;

	pO = P;
	sort(P+1,P+n,comp4Graham);
    
    //去掉这个
	//p = unique(P+1,P+n,isEqPolar);
	//n = p - P;
	int top = 2;
	for(int i=2;i<n;++i){
		while( top > 1 && cross(P[top-2],P[top-1],P[i]) < 0LL )
			--top;
		P[top++] = P[i];
	}
	return top;
}
int main(){
	int n;
	while( scanf("%d",&n) && n ){
        for(int i=0;i<n;++i)scanf("%I64d%I64d",&P[i].x,&P[i].y);
		n = Graham(n);

		printf("%d\n",n);
		for(int i=0;i<n;++i)printf("%I64d %I64d\n",P[i].x,P[i].y);
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值