“现代汽车中国前瞻软件赛杯” 牛客周赛 Round 43 E题
小红的平行四边形
题目背景
题目描述
小红在平面上有 n n n个点,她准备选择其中四个点画一个平行四边形。请你帮小红求出平行四边形的最大面积。
样例 #1
样例输入 #1
5
0 0
0 1
1 0
1 1
2 1
样例输出 #1
1.0
说明
选前四个点即可(正方形也是平行四边形)
做题思路
这道题需要一些前置的几何知识
首先一点就是,两个不共线的向量的叉积(向量积,外积)的几何意义是以这两个向量为邻边的平行四边形的面积
外积
为啥要向量呢?
观察到 n ≤ 1000 n\le 1000 n≤1000,如果要靠点去确定一个平行四边形,至少需要三个点,然后确定另一个点的位置;最后确定平行四边形的面积。
如果是三层循环枚举所有情况时间复杂度就来到了 O ( n 3 ) O(n^3) O(n3),最坏情况约为 O ( 1 0 9 ) O(10^9) O(109),会超时。
所以改点为边,两点确定一条边,那么得出所有边的时间复杂度为 O ( n 2 ) O(n^2) O(n2),暂时没有任何问题。
但如果只是简单的看成边,只知道长度是远远不够的。
因为向量计算平行四边形是比较容易的,所以往向量上考虑。
向量有长度和方向。而且这道题需要记录起点。因为形成平行四边形是靠点连线,而不可以平移向量。
又知道平行四边形的长度对边相同且平行,所以同一长度且平行的向量(边)才能形成平行四边形。
这里开一个map,键为向量的长度和方向(即坐标),值为坐标和起点。
如果有两个坐标相同的向量,那么如何去算平行四边形呢?
因为都记录了向量的起点,那么两个起点能构成新向量,新向量和两个坐标相同的向量(拿一个)进行外积就可以得到新向量和旧向量形成的平行四边形的面积。
那么记录面积最大的即可。
有个坑点就是其实答案是整数,所以小数位一定是.0
原因是坐标为整数,向量外积也是整数,那么面积也一定是整数。
分析一个大佬JinYuManTang的题解
原文:
由于需要三个点才能计算平行四边形面积,枚举点时间复杂度不符合要求,所以我们考虑枚举边。我们对每两个点构成的向量用哈希表分组,注意向量的方向性。这里不用存下每组所有的向量然后进行排序,只需要维护一个最大值和最小值,不过需要用到一点简***面几何。
我们将当前分组的向量平移到原点,由于需要求每组向量之间能够组成的最大面积(如图中的红色面积),这可以拆成两个平行四边形面积之和(如图中绿色的S1和S2)。由于S是用叉积计算的(带正负),所以其实红色面积是S2 - S1。因此,我们只需要维护每组S的最大值和最小值,两者之差即为该组的最大面积。
作者:JinYuManTang
链接:https://www.nowcoder.com/discuss/622155303887396864?sourceSSR=users
来源:牛客网
from collections import defaultdict
n = int(input())
a = [list(map(int, input().split())) for _ in range(n)]
mx, mn = defaultdict(lambda: -10 ** 18), defaultdict(lambda: 10 ** 18)
res = 0
for i in range(n):
x1, y1 = a[i]
for j in range(i):
x2, y2 = a[j]
dx, dy = x1 - x2, y1 - y2
if dx > 0: dx, dy = -dx, -dy
s = dy * x1 - dx * y1
p = (dx, dy)
mx[p], mn[p] = max(mx[p], s), min(mn[p], s)
res = max(res, mx[p] - mn[p])
print(str(res) + '.0' if res else -1)
作者:JinYuManTang
链接:https://www.nowcoder.com/discuss/622155303887396864?sourceSSR=users
来源:牛客网
这里对红色面积为什么是 S 2 − S 1 S2-S1 S2−S1做个较为详细的解释
S
1
=
a
⃗
×
c
⃗
S1 = \vec{a}\times \vec{c}
S1=a×c
S
2
=
a
⃗
×
b
⃗
S2 = \vec{a}\times \vec{b}
S2=a×b
d
⃗
=
b
⃗
−
c
⃗
\vec{d} = \vec{b} - \vec{c}
d=b−c
红色面积
=
a
⃗
×
d
⃗
=
a
⃗
×
(
b
⃗
−
c
⃗
)
=
a
⃗
×
b
⃗
−
a
⃗
×
c
⃗
=
S
2
−
S
1
红色面积 = \vec{a}\times \vec{d} = \vec{a}\times(\vec{b} - \vec{c}) = \vec{a}\times \vec{b} - \vec{a}\times \vec{c} = S2-S1
红色面积=a×d=a×(b−c)=a×b−a×c=S2−S1
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <tuple>
#include <cstring>
#define int long long
using namespace std;
const int N = 1e5+10;
inline bool cmp(vector<int>a,vector<int> b){
int x=a[2],y=a[3],xx=b[2],yy=b[3];
int dx=a[0],dy=a[1];
return dx*y-dy*x < dx*yy-dy*xx;
}
int a[N],b[N];
int n;
map<pair<int,int> , vector<vector<int>>> mp;
signed main(){
cin >> n;
for(int i=1;i<=n;i++)cin >> a[i] >> b[i];
int x,y;
for(int i=1;i<=n;i++){
for(int j=1+i;j<=n;j++){
x = a[i]-a[j] , y = b[i] - b[j];
if(x<0)x*=-1,y*=-1;
mp[make_pair(x,y)].push_back({x,y,a[i],b[i]});
}
}
int ans = 0;
for(auto i : mp){
auto v = i.second;
sort(v.begin(),v.end(),cmp);
int x=v[0][2],y=v[0][3],xx=v.back()[2],yy=v.back()[3];
int dx = xx-x, dy = yy-y;
ans=max(ans,abs(i.first.second*dx - i.first.first*dy));
}
if(!ans)cout << -1;
else cout << ans << ".0";
return 0;
}