题目:给你n个点,求能组成多少个正方形?
范围最大1000个点,时间3.5s,暴力枚举不用想肯定会超时。超时并不代表枚举行不通,实际上我们可以枚举两个点,然后在考虑下面的情况。
首先搞清楚两个问题,1、给你两个点,你能否求出用这两个点组成正方形一条边的其他两个点?2、如果给你一个点,你能否快速O(1)时间内告诉我这个点是否在给出的所有点当中。
如果你解决了上面两个问题,这个题目就好办了。
首先第一个问题,给你两个点,求正方形其他两个点坐标,注意,这里千万不要把这两个点当做对角线上的两个点,这样的话,你将会求不出正方形的个数。我们就以这两个点连成的线段当做正方形的一条边来做,不考虑对角线情况。
那么,给你正方形一条边,肯定有两种组成正方形的情况,关于这条边对称的肯定有两个正方形,还是给个图吧!
如以AB线段为边可以有1 2两个正方形。
如果A点坐标为x1,y1, B点x2,y2,可以求出两组(x3,y3)和(x4,y4)。
根据上图1三角形和2三角形全等周围的框都是平行于x轴和y轴的,很容易求得另外两个点的两组坐标
已知: (x1,y1) (x2,y2)
则: x3=x1+(y1-y2) y3= y1-(x1-x2)
x4=x2+(y1-y2) y4= y2-(x1-x2)
或
x3=x1-(y1-y2) y3= y1+(x1-x2)
x4=x2-(y1-y2) y4= y2+(x1-x2)
第二个问题,就是如果知道了C,D两个点如何判断这两个点是否在点集里面。
方法很多,可以用C++ STL,同时也可以用hash。
hash,有很多方法构造散列表,这里用平方求余法。关于hash方面的知识,这里就不介绍了。
下面就只剩下代码了:
#include <iostream>
using namespace std;
struct Node
{
int x,y;
Node * next;
}node[40001];
int n;
int cod[1005][2];
int Find(int x, int y)
{
int key = (x * x + y * y) % 40001;
Node *tmp = &node[key];
while (tmp->next != NULL)
{
tmp = tmp->next;
if (tmp->x == x && tmp->y == y)
{
return 1;
}
}
return 0;
}
int main()
{
while (scanf("%d", &n) && n)
{
memset(node, 0, sizeof(node));
Node * point;
for (int i = 1; i <= n; ++ i)
{
point = new Node;
scanf("%d %d", &cod[i][0], &cod[i][1]);
point->x = cod[i][0];
point->y = cod[i][1];
point->next = NULL;
int key = (cod[i][0] * cod[i][0] + cod[i][1] * cod[i][1]) % 40001;
Node *tmp = &node[key];
while (tmp->next != NULL)
{
tmp = tmp->next;
}
tmp->next = point;
}
int ans = 0;
int x1,y1,x2,y2;
for (int i = 1; i < n; ++ i)
{
for (int j = i + 1; j <= n; ++ j)
{
//two points on one side
x1 = cod[i][0] + (cod[i][1] - cod[j][1]);
y1 = cod[i][1] - (cod[i][0] - cod[j][0]);
x2 = cod[j][0] + (cod[i][1] - cod[j][1]);
y2 = cod[j][1] - (cod[i][0] - cod[j][0]);
if (Find(x1,y1) && Find(x2,y2))
{
ans ++;
}
//
x1 = cod[i][0] - (cod[i][1] - cod[j][1]);
y1 = cod[i][1] + (cod[i][0] - cod[j][0]);
x2 = cod[j][0] - (cod[i][1] - cod[j][1]);
y2 = cod[j][1] + (cod[i][0] - cod[j][0]);
if (Find(x1,y1) && Find(x2,y2))
{
ans ++;
}
}
}
printf("%d\n", ans / 4);
}
return 0;
}
注意,最后求出的总数要除以4,为什么呢?比如一个正方形四个点顺时针依次是 1 2 3 4.那么我们枚举点的时候,肯定是1-2 1-3 1-4 2-3 2-4 3 -4,这里面可以有正方形的是1-2, 1-4, 2-3, 3-4所以重复了4遍。