HDU 5128 The E-pang Palace(枚举,几何)

题目:The E-pang Palace

题意:给定N个点,选出其中8个点组成两个矩形,使得两个矩形的面积和最大。

限制条件:

1、一定得构成两个矩形

2、两个矩形不能有交叉,不能共用顶点,边不能有部分重合。

如果不能满足条件,输出imp。

现场赛的时候这题坑了不少人,我那会是看着很多人没过才细想,要不然,估计我也得WA。

这题由于N才30,所以可以枚举了事。

但是有一个坑点,就是虽然矩形相交不可以,但是可以嵌套,如果一个大矩形套一个小矩形,面积只能算大的。因为题目的实际背景是求两个矩形围起来的地的总面积。

我的做法可能麻烦了些,不想看超长代码的请绕道。。。

虽说我现场赛就是这么写的,,,写的快晕,幸好只是因为freopen的问题2Y。

对于N<8的情形是直接输出imp,因为根本没法构成两个矩形。

先将点按照X坐标大小排序。

记找到的第一个矩形是A,第二个是B。

然后枚举左下角的顶点,然后找到跟它相同X坐标以及Y坐标的顶点,作为左上角和右下角。看看能不能找到右上角,找到了作为第一组点。

接着枚举第二个矩形的左下角。如果是刚好在某条A的边上,排除。

第一个确定了,枚举后面三个点的方法同上。

注意,如果第一个点是在A内的,后面三个点也要。如果满足了,那么此时的面积是A的面积。

如果第一个点在A外面,那么后三个点也是在A外面。

枚举得到A和B了,虽然保证B不在A里面,但是有可能A和B交叉,判断方法是统计A的顶点有多少个在B里面。

如果有4个,说明B包含了A,面积是A的面积。

如果没有,说明A和B独立,把A和B的面积加起来。

剩下说明有交叉,忽略。

#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const int N = 30;
const int M = 210;
map<int, map<int,int> > MP;
vector<int> X[M], Y[M];
typedef vector<int>::iterator vit;
#define all(it, V) for(vit it=V.begin(); it!=V.end(); it++)
struct Point{
	int x, y;
	bool operator < (const Point &A)const{
		return x<A.x || (x==A.x && y<A.y);
	}
}p[N];
int n, ans, a[4], b[4];
bool flag[N];
bool on_seg(int *arr, int i){
	if(p[arr[0]].x==p[i].x){
		return p[arr[0]].y<=p[i].y && p[i].y<=p[arr[1]].y;
	}
	if(p[arr[2]].x==p[i].x){
		return p[arr[2]].y<=p[i].y && p[i].y<=p[arr[3]].y;
	}
	if(p[arr[0]].y==p[i].y){
		return p[arr[0]].x<=p[i].x && p[i].x<=p[arr[2]].x;
	}
	if(p[arr[1]].y==p[i].y){
		return p[arr[1]].x<=p[i].x && p[i].x<=p[arr[3]].x;
	}
	return 0;
}
bool in(int *arr, int i){
	return (p[arr[0]].x<p[i].x && p[i].x<p[arr[2]].x) && (p[arr[0]].y<p[i].y && p[i].y<p[arr[1]].y);
}
bool in2(int *arr, int i){
	return (p[arr[0]].x<=p[i].x && p[i].x<=p[arr[2]].x) && (p[arr[0]].y<=p[i].y && p[i].y<=p[arr[1]].y);
}
int area(int *arr){
	return (p[arr[2]].x-p[arr[0]].x) * (p[arr[1]].y-p[arr[0]].y);
}
void solve1(){
	all(i, X[p[b[0]].x]){
		if(p[*i].y<=p[b[0]].y || flag[*i])	continue;
		if(!in(a, *i))	continue;
		b[1] = *i;
		all(j, Y[p[b[0]].y]){
			if(p[*j].x<=p[b[0]].x || flag[*j])	continue;
			if(!in(a, *j))	continue;
			b[2] = *i;
			if(MP[p[b[2]].x].count(p[b[1]].y)){
				b[3] = MP[p[b[2]].x][p[b[1]].y];
				if(!flag[b[3]])
					ans = max(ans, area(a));
			}
		}
	}
}
void cal(){
	int cnt = 0;
	for(int i=0; i<4; i++){
		if(in(b, a[i]))	cnt++;
		if(on_seg(b, a[i]))	return;
	}
	if(cnt == 0)	ans = max(ans, area(a)+area(b));
	else if(cnt == 4)	ans = max(ans, area(b));
}
void solve2(){
	all(i, X[p[b[0]].x]){
		if(p[*i].y<=p[b[0]].y || flag[*i])	continue;
		if(in2(a, *i))	continue;
		b[1] = *i;
		all(j, Y[p[b[0]].y]){
			if(p[*j].x<=p[b[0]].x || flag[*j])	continue;
			if(in2(a, *j))	continue;
			b[2] = *j;
			if(MP[p[b[2]].x].count(p[b[1]].y)){
				b[3] = MP[p[b[2]].x][p[b[1]].y];
				if(!in2(a, b[3]) && !flag[b[3]]){
					cal();
				}
			}
		}
	}
}
void solve(){
	memset(flag, 0, sizeof(flag));
	for(int i=0; i<4; i++)	flag[a[i]] = 1;
	for(int i=0; i<n; i++){
		if(flag[i])	continue;
		if(on_seg(a, i))	continue;
		b[0] = i;
		if(in(a, i))	solve1();
		else	solve2();
	}
}
int main(){
	while(~scanf("%d", &n) && n){
		for(int i=0; i<n; i++){
			scanf("%d %d", &p[i].x, &p[i].y);
			X[p[i].x].clear();
			Y[p[i].y].clear();
		}
		if(n<8){
			puts("imp");
			continue;
		}
		sort(p, p+n);
		MP.clear();
		for(int i=0; i<n; i++){
			MP[p[i].x][p[i].y] = i;
			X[p[i].x].push_back(i);
			Y[p[i].y].push_back(i);
		}
		ans = -1;
		for(int i=0; i<n; i++){
			a[0] = i;
			all(j, X[p[a[0]].x]){
				if(p[*j].y <= p[a[0]].y)	continue;
				a[1] = *j;
				all(k, Y[p[a[0]].y]){
					if(p[*k].x <= p[a[0]].x)	continue;
					a[2] = *k;
					if(MP[p[a[2]].x].count(p[a[1]].y)){
						a[3] = MP[p[a[2]].x][p[a[1]].y];
						solve();
					}
				}
			}
		}
		if(ans==-1)	puts("imp");
		else	printf("%d\n", ans);
	}
	return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值