1. 程式人生 > 其它 >【題解】金牌導航-凸包-穩定凸包

【題解】金牌導航-凸包-穩定凸包

題面:

做法詳解:

考慮一個什麼樣的凸包是一個穩定的凸包(推薦自己畫畫圖,就理解了)
若對於凸包上的每一條邊,都有包含其端點在內至少三個不同位置的點,那麼這就肯定是一個穩定的凸包。
考慮若只有兩個點,那麼在其外面在加入一個點,那麼這個點與其對應的這條邊可以同時被計算入新的凸包
考慮若有三個及更多的點,那麼在其外面加入一個點,這條邊上除了兩個端點之外的那些點就不可能再計入新的凸包裡了,所以這就是一個穩定的凸包
實現起來非常好搞:對於每一條凸包上的邊統計有多少個點在這條邊上,注意這些點不一定要是凸包上的點
小細節:
(1)可能會有重複的點,那麼這樣的點肯定不能被算多次,所以要去重
(2)考慮若凸包就是一條線,那麼肯定不會是一個穩定的凸包,因為在其延長線上再隨意找一個點也可以形成一個包含所有點的新凸包

程式碼實現:





文字版:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3+4;
struct V{
	int x,y;
	V(int _x = 0 ,int _y = 0){
		x = _x,y = _y;
	}
}q[MAXN],a[MAXN],tmp[MAXN];
int head,n;
V operator - (V l,V r){
	return V(l.x - r.x,l.y - r.y);
}
int operator * (V l,V r){
	return l.x * r.y - l.y * r.x;
}
int dot(V l,V r){
	return l.x * r.x + l.y * r.y;
}
int len(V l){
	return sqrt(l.x * l.x + l.y * l.y);
}
bool cmp(V l,V r){
	V u = (l - a[1]),w = (r - a[1]);
	int h = u * w;
	if(h != 0){
		return h > 0;
	}
	if(l.x != r.x)
		return l.x < r.x;
	return l.y<r.y;
}
bool cmp_x(V l,V r){
	if(l.x != r.x)
		return l.x < r.x;
	return l.y < r.y;
}
void Graham(){
	int pos = 1;
	for(int i=2; i<=n; i++){
		if(a[i].x < a[pos].x || (a[i].x == a[pos].x && a[i].y < a[pos].y)){
			pos = i;
		}
	}
	if(pos != 1){
		swap(a[pos],a[1]);
	}
	sort(a+2,a+n+1,cmp);
	for(int i=1; i<=n; i++){
		while(head >= 2 && (a[i] - q[head -1]) * (q[head] - q[head - 1]) >= 0){
			head--;
		}
		q[++head] = a[i];
	}
	q[head + 1] = q[1];
}
bool in_line(V p,V from,V to){
	int h = (from - p) * (to - p);
	if(h != 0)
		return false;
	return dot((from - p),(to - p)) <= 0;
}
bool check_ans(V from,V to){
	int res = 0;
	for(int i=1; i<=n; i++){
		if(in_line(a[i],from,to))
			res++;
		if(res >= 3)
			return true;
	}
	return res >= 3;
}
int cross(V x,V y,V z){
	return (y - x) * (z - x);
}
int Area(){
	int res = 0;
	for(int i=1; i<=head; i++){
		res += q[i] * q[i+1];
	}
	return res;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		head = 0;
		int h;
		cin>>h;
		for(int i=1; i<=h; i++){
			cin>>tmp[i].x>>tmp[i].y;
		}
		sort(tmp+1,tmp+h+1,cmp_x);
		a[1] = tmp[1];
		n = 1;
		for(int i=2; i<=h; i++){
			if(tmp[i].x != tmp[i-1].x || tmp[i].y != tmp[i-1].y)
				a[++n] = tmp[i];
		}
		Graham();
		bool flag = false;
		for(int i=1; i<=head; i++){
			if(!check_ans(q[i],q[i+1])){
				printf("NO\n");
				flag = true;
				break;
			}
		}
		if(Area() == 0 && !flag){
			printf("NO\n");
		}
		else if(!flag){
			printf("YES\n");
		}
	}
	return 0;
}