P3217 [HNOI2011] 数矩形
由于其他 dalao 的题解本蒟蒻看不懂,所以写了这篇比较好理解的题解。
前置知识
- 只有当两条线段的中点重合,线段长度相同,才会构成矩形。
- 勾股定理求线段
AB 的长度:\sqrt{\left|X_A-X_B\right|^2+\left|Y_A-Y_B\right|^2} 。 - 此代码要开吸氧优化,时间能下降到原时间的
\frac{1}{3} ,还是比较可观的。
步骤
一、储存每条线段的信息
- 在这
n 个点中,一共有\frac{n(n-1)}{2} 条线段,以它们的长度为分类的标准,储存该线段的两个节点的x,y 坐标和中点的坐标。 - 线段长度的计算:线段长度可用勾股定理求,但求出的可能是无理数,所以不开根号存储更好。
二、计算矩形面积
- 对于线段长度相同的线段,以中点坐标为根据排序,找到中点坐标相同的几条线段,计算其中任意两条线段组成的矩形的面积。
- 既然找到了两条线段共四个点,也可用勾股定理求出矩形的长和宽,这样可求出此矩形的面积。
三、输出
选出矩形面积中最大的即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1.5e3+10;
const int M=1e6+10;
int n,x[N],y[N],cnt=0,ans=0;
struct node{//存储线段两个端点和中点的坐标
int x1,y1,x2,y2;
long double mid_x,mid_y;
};
map<int,int>mp;
vector<node>g[M];
int rec(int p){//辅助勾股定理的计算
return abs(p)*abs(p);
}
bool cmp(node p,node q){//按中点排序
if(p.mid_x==q.mid_x)return p.mid_y<q.mid_y;
return p.mid_x<q.mid_x;
}
signed main(){
cin >>n;//输入
for(int i=1;i<=n;i++){
cin >>x[i]>>y[i];
}
for(int i=1;i<=n-1;i++){//存储线段
for(int j=i+1;j<=n;j++){
int len=rec(x[i]-x[j])+rec(y[i]-y[j]);//勾股定理
if(!mp[len])mp[len]=++cnt;
g[mp[len]].push_back({x[i],y[i],x[j],y[j],(x[i]+x[j])/2.0,(y[i]+y[j])/2.0});
}
}
for(int i=1;i<=cnt;i++){//计算矩形面积
sort(g[i].begin(),g[i].end(),cmp);
for(int j=0;j<g[i].size();j++){
//判断两条线段的中点是否相同
int id=j;
while(g[i][id].mid_x==g[i][j].mid_x&&g[i][id].mid_y==g[i][j].mid_y&&id<g[i].size()){
id++;
}
for(int k=j;k<id-1;k++){//计算矩形面积
for(int l=k+1;l<=id-1;l++){
node p=g[i][k],q=g[i][l];
int a=rec(p.x1-q.x1)+rec(p.y1-q.y1);
int b=rec(p.x1-q.x2)+rec(p.y1-q.y2);
ans=max(ans,(int)(sqrt(a)*sqrt(b)));//求最大值
//这里直接求长的平方乘宽的平方会炸
}
}
}
}
cout <<ans;//输出
return 0;
}