HDU 6219 空凸包 + DP

本文介绍了一种基于Graham扫描法和动态规划的算法,用于解决给定点集中空凸包的最大面积问题。通过枚举点集中的点,构建空凸多边形,并计算其面积,最终找出最大面积的空凸包。

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

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值