1. 程式人生 > >HDU 6219 空凸包 + DP

HDU 6219 空凸包 + DP

Empty Convex Polygons

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total Submission(s): 651    Accepted Submission(s): 175

Problem Description

Given a set of distinct points S on a plane, we define a convex hole to be a convex polygon having any of thegiven points as vertices and not containing any of the given points in its interior. In addition to the vertices, other given points may lie on the perimeter of the polygon. We want to find a convex hole as above forming the convexpolygon with the largest area.

Input

This problem has several test cases. The first line of input contains an integer t (1 ≤ t ≤ 100) indicating the total number of cases. For each test case,the first line contains the integer n (3 ≤ n ≤ 50). Each of the following n lines describes a point with two integers x and y where -1000 ≤ x, y ≤ 1000. We guarantee that there exists at least one non-degenerated convex polygon.

Output

For each test case, output the largest area of empty convex polygon, with the precision of 1 digit. Remark: The corollary of Pick’s theorem about the polygon with integer coordinates in that says the area of it iseither ends to .0 or .5.

Sample Inpu

4

3

0 0

1 0

0 1

5

0 0

1 0

2 0

0 1

1 1

5

0 0

3 0

4 1

3 5

-1 3

6

3 1

1 0

2 0

3 0

4 0

5 0

Sample Output

0.5

1.5

17.0

2.0

題意:給出 n 個點,要求從這 n 個點中選擇出若干個點,構成一個空凸包(即土包內不包含其他點),且要求最大符合要求的凸包的面積

做空凸包:每次列舉兩個點與原點構成三角形後,判斷之前的點是否有在這個三角形內的,如果有,這個三角形不能構成空凸包,跳過,如果沒有,那麼可以計算面積

dp[i][j] 表示 以 點i 和 點j 連成的直線作為倒數第二條線的凸包的最大面積

那麼 dp[i][j] = max(dp[i][j],dp[j][k] + 空三角形面積)    k 為 j 之前的點

為了找到最大面積的凸包,要列舉整個凸包左下角的那個點作為起點,然後做凸包,取最大面積

/**
Author: LinZhiQ
Date: 2018-10-01 13:31
Graham掃描法 + DP 求空凸包
*/
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);

struct Point {
	double x,y;

	Point() {}

	Point(double _x, double _y) {
		x = _x;
		y = _y;
	}

	Point operator -(const Point &b)const {
		return Point(x - b.x, y - b.y);
	}// (點 a - 點 b) 表示以 b 為座標軸原點重新定義 a 的座標

	double operator ^(const Point &b)const {
		return x * b.y - y * b.x;
	}//Cross 比較 點 a 與 點 b 到原點 連線的斜率 ,a的斜率小則返回值 大於零,等於零表示在同一直線
	double operator *(const Point &b)const {
		return x * b.x + y * b.y;
	}//Dot	點自己 乘 自己 表示 點到原點距離的平方 也就是點乘

	void transXY(double B) {
		double tx = x,ty = y;
		x = tx * cos(B) - ty * sin(B);
		y = tx * sin(B) + ty * cos(B);
	}
} p[105], t[105];

bool cmp1(const Point &a, const Point &b) {
	return (a.y != b.y ? a.y < b.y : a.x < b.x);
}	// 按照點的 y 座標從小到大,x 座標從小到大 (逆時針)

bool cmp2(const Point &a, const Point &b) {
	return ((a ^ b) == 0 ? (a * a) < (b * b) : (a ^ b) > 0);
}	// 極角排序,如果 a與b 在同一直線,那就與原點距離近的優先,否則極角小的優先 

int T, n, m;
double dp[105][105], ans;

void init() {
	for(int i = 0; i <= m; i++) {
		for(int j = 0; j <= i; j++) {
			dp[i][j] = 0;
		}
	}
}

void solve() {
	init();
	for(int i = 2; i <= m; i++) {
		for(int j = 1; j < i; j++) {
			if((t[i] ^ t[j]) == 0)	// 如果與前點斜率相同(同一直線,則跳過) 
				continue;
			bool flag = true;
			// 此時選定了兩個點  t[i] 和 t[j] 
			for(int k = j + 1; k < i; k++) { // 遍歷第三個點 
				if((t[k] ^ t[i]) > 0 && (t[j] ^ t[k]) > 0 && ((t[i] - t[j]) ^ (t[k] - t[j])) > 0) {
					// 如果第三個點被 原點 i 和 j 包圍,那麼說明這個凸包不是空凸包 
					flag = false;
					break;
				}
			}
			if(!flag) {
				continue;
			}
			// 如果這個三個點圍成的三角形是個空凸包 
			dp[i][j] = t[j] ^ t[i]; // 多邊形面積計算公式 p[a] ^ p[b] / 2 
			if((t[j] ^ t[j - 1]) != 0) {	// 如果上一個點在它右邊,那麼面積可以拓展 
				for(int k = 1; k < j; k++) {
					if(((t[k] - t[i]) ^ (t[j] - t[i])) > 0) {
						dp[i][j] = max(dp[i][j],dp[j][k] + (t[j] ^ t[i]));
					}
				}
			}
			ans = max(ans,dp[i][j]);
		}
	}
}
int main() {
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		for(int i = 1; i <= n; i++) {
			scanf("%lf %lf",&p[i].x,&p[i].y);
		}
		sort(p + 1, p + 1 + n, cmp1);
		ans = 0;
		for(int i = 1; i <= n - 2; i++) { //列舉左下角的起點找最大空凸包 
			m = 0;
			for(int j = i + 1; j <= n; j++) {
				t[++m] = p[j] - p[i]; // 以 p[i] 為座標軸中心,重新定義 p[j]
			}
			sort(t + 1, t + 1 + m, cmp2);
			solve();
		}
		printf("%.1lf\n",ans / 2.0);
	}
	return 0;
}