[HNOI2011]数矩形

本文探讨了如何从平面上的多个点中选取四个点构成矩形,目标是找到面积最大的矩形。通过分析线段作为矩形对角线的条件,即中点相同和长度相同,提出了一种O(n^2)的解决方案。该方案首先处理所有可能的线段,然后排序并找到满足条件的区间,最后通过枚举得出最大矩形面积。

题目描述
最近某歌手在研究自己的全球巡回演出计划,他将所有心仪的城市都用平面上的一个点来表示,并打算从中挑选出 4 个城市作为这次巡回演出的地点。

为了显示自己与众不同,他要求存在一个矩形使得挑选出的 4 个点恰好是这个矩形的 4 个顶点,并且希望这个矩形的面积最大。

这可急坏了其经纪人,于是他向全球歌迷征集方案,当然你这位歌迷一定不会错过这个机会。

输入格式
从文件input.txt中读入数据,输入文件的第一行是一个正整数NN,表示平面上点的个数(即某歌手心仪的城市数)。接下来的NN行,每行是由空格隔开的两个整数X_iX
i
​ 和Y_iY
i
​ ,表示其对应点的坐标。20%的数据满足N\leq 500N≤500,100%的数据满足N\leq 1500N≤1500,-10^8\leq X_i,Y_i\leq 10^8−10
8
≤X
i
​ ,Y
i
​ ≤10
8
,且输入数据保证答案存在。

输出格式
输出文件 output.txt 仅包含一个非负整数,表示最大的矩形面积。

输入输出样例
输入 #1
8
-2 3
-2 -1
0 3
0 -1
1 -1
2 1
-3 1
-2 1
输出 #1
10

.
.
.
.
.
.
分析
两条线段能作于矩形的对角线有2个条件
1.中点相同
2.长度相同
于是O(n^2)处理出所有直线,排序
找到满足条件的区间,枚举得出答案

.
.
.
.
.
.
程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

long long ans,cnt;

struct dot
{
	long long x,y;
}a[1505];

struct edge
{
	long long x,y,l,a,b;
}b[2000005];

bool cmp(edge a,edge b)
{
	if (a.x!=b.x) return a.x<b.x; else
	if (a.y!=b.y) return a.y<b.y; else return a.l<b.l;
}

long long work(long long x,long long y,long long z)
{
	return abs((a[x].x-a[y].x)*(a[y].y-a[z].y)-(a[x].y-a[y].y)*(a[y].x-a[z].x));
}

int main()
{
	int n;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lld%lld",&a[i].x,&a[i].y);
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
		{
			b[++cnt].x=a[i].x+a[j].x;
			b[cnt].y=a[i].y+a[j].y;
			b[cnt].l=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y);
			b[cnt].a=i;
			b[cnt].b=j;
		}	
	sort(b+1,b+cnt+1,cmp);
	for (int i=1,j;i<=cnt;i=j+1)
	{
		j=i+1;
		while (j<=cnt&&b[j].x==b[i].x&&b[j].y==b[i].y&&b[j].l==b[i].l) j++;
		j--; 
		for (int i1=i;i1<=j;i1++)
			for (int j1=i1+1;j1<=j;j1++)
				ans=max(ans,work(b[i1].a,b[j1].a,b[i1].b));
	}
	printf("%lld",ans);
	return 0;
}
4.4 激光炸弹 题⽬来源: 洛⾕ 题⽬链接: [HNOI2003]激光炸弹 难度系: ★★ 【题⽬描述】 ⼀种新型的激光炸弹,可以摧毁⼀个边⻓为 R 的正⽅形内的所有的⽬标。 现在地图上有 个⽬标,⽤整 , (其值在 )表⽰⽬标在地图上 的位置,每个⽬标都有⼀个价值。 N(1 ≤ N ≤ 10000) Xi Yi [0, 5000] 激光炸弹的投放是通过卫星定位的,但其有⼀个缺点,就是其爆破范围,即那个边⻓为 的正⽅形 的边必须和 轴平⾏。 R x, y 若⽬标位于爆破正⽅形的边上,该⽬标将不会被摧毁。 对于 的据,保证 。 100% 1 ≤ n ≤ 10 4 , 0 ≤ xi, yi ≤ 5 × 10 3 , 1 ≤ m ≤ 5 × 10 3 , 1 ≤ vi ≤ 100 【输⼊描述】 输⼊⽂件的第⼀⾏为正整 和正整 ,接下来的 ⾏每⾏有 个正整,分别表⽰ 。 N R N 3 xi, yi, vi 【输出描述】 输出⽂件仅有⼀个正整,表⽰⼀颗炸弹最多能炸掉地图上总价值为多少的⽬标(结果不会超过 32767 )。 【⽰例⼀】 输⼊: 2 1 27 28 29 30 31 32 33 34 35 0 0 1 1 1 1 输出: 1 【解法】 可以⽤⼀个⼆维矩阵将所有⽬标的价值存起来,其中 a[i][j] 就表⽰ [i, j] 位置的⽬标价值之和。 ⼀颗炸弹能够获得的价值正好是⼀个 ⼤⼩的⼀个正⽅形内所有⽬标的价值总和,那么我们可 以求出 矩阵的前缀和矩阵,然后枚举所有边⻓为 的⼦正⽅形的价值之和,求出⾥⾯的最⼤值即 可。 R × R a R 如何枚举边⻓为 R 的所有正⽅形: • 仅需枚举右下⻆ ,那么结合边⻓ 就可算 出左上⻆ 。 [x2 , y2 ] (R + 1 ≤ x2 ≤ 5000, R + 1 ≤ y2 ≤ 5000) R [x2 − R + 1, y2 − R + 1] • 代⼊前缀和矩阵中,就可以快速求出这个正⽅形内所有⽬标的总价值。 细节问题: 1. 题⽬中某⼀个位置会「重复」出现,因此 a[i][j]+ = w ; 2. 半径 R 可能「超过 5000 」 ,此时炸弹可以摧毁所有⽬标,也就是整个矩阵的⽬标价值之和。 【参考代码】 代码块 #include <iostream> using namespace std; const int N = 5010; int n, m; int a[N][N]; int f[N][N]; // 前缀和矩阵 int main() { cin >> n >> m; while(n--) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { int x, y, v; cin >> x >> y >> v; x++, y++; // 下标从 1 开始计 a[x][y] += v; // 同⼀个位置有可能有多个⽬标 } n = 5001; // 预处理前缀和矩阵 for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + a[i][j]; } } int ret = 0; m = min(m, n); // 如果 m 很⼤,相当于就是把整个区域全部摧毁 // 枚举所有边⻓为 m 的正⽅形 for(int x2 = m; x2 <= n; x2++) { for(int y2 = m; y2 <= n; y2++) { int x1 = x2 - m + 1, y1 = y2 - m + 1; ret = max(ret, f[x2][y2] - f[x1 - 1][y2] - f[x2][y1 - 1] + f[x1 - 1][y1 - 1]); } } cout << ret << endl; return 0; }
最新发布
09-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值