P1665 正方形计数 题解
题面
这是一道大水题
当然,可以用搜索什么的过,但是会慢一点
那么我们来理一理思路
我们枚举每一个可能正方形的对角线
首先我们有这样的一个正方形:
其中黑色的两个点是确定的,我们要得到另外两个红点的坐标……
使用毕达哥拉斯平方定理!使用锐角三角正切函数!求出斜率!暴力代入! 这些方法会损精度的……
我们要用一种挺巧妙的方法:
连原来的两个点,求出中点:
然后从终点向x,y轴各引一条垂线与正方形的边相交
我们很容易就会证到
图中两个上色的三角形全等。
然后就很容易明白两条蓝线等长了啦QωQ
上面那条蓝线就是中点和另一个点的纵坐标差,
下面那条就是中点和待求点的横坐标差了,
酱紫就可以由中点坐标求到两个红点的纵坐标了嘛!
求横坐标也是如此啦……
然后呢,接下来有两种做法:
第一是用哈希表来找求出来的两个红点有没有出现过
第二是用一个布尔数组来存每个坐标是否有点
当然哈希会省点内存而且能处理带小数的情况
但是我很懒QωQ就用第二种了
还要注意一点!
在枚举时会把原来当过红点的点重新算一遍
所以最终答案要除以二
#include<cstdio>
#include<cstring>
using namespace std;
struct point
{
int x,y;//为什么不用double呢?因为由全等可以证明:当一个正方形两点都在整数坐标上时,
//其他两点也会在整点上
}a[510];
bool v[310][310];
int main()
{
int n;scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++)
scanf("%d%d",&a[i].x,&a[i].y),
(a[i].x+=51)<<=1,(a[i].y+=51)<<=1,//防止负数,另外还要防止中点出现小数坐标
v[a[i].x][a[i].y]=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
int xmid=(a[i].x+a[j].x)>>1,ymid=(a[i].y+a[j].y)>>1;//中点的坐标
int x1=xmid-(a[i].y-ymid),y1=ymid-(xmid-a[i].x);//这是下面那个点
int x2=xmid+(a[i].y-ymid),y2=ymid+(xmid-a[i].x);//上面那个点明显就是反过来的啦
if(0<=x1&&x1<=300&&0<=y1&&y1<=300&&0<=x2&&x2<=300&&0<=y2&&y2<=300)
if(v[x1][y1]&&v[x2][y2])ans++;
}
printf("%d",ans>>1);
return 0;
}
有问题欢迎私信哦QωQ