1. 程式人生 > >計算幾何 graham 最大凸包

計算幾何 graham 最大凸包

凸包的嚴格凸多邊形
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;

凸包的不嚴格凸多邊形
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;
}