题意:给出平面上的n个点,求出能组成的正方形个数。(n <= 1000)
先考虑暴力,O(n^4),会超时。
再考虑正方形的特点,已知两个点,可以推出另外两个点。所以枚举两个点,因为点的坐标|x,y|<=200000,所以需要哈希处理。复杂度O(n^2)。
枚举两个点时,我曾经纠结于是否枚举对角线。但是细想一下,包含对角线的正方形其实应经在枚举过程中出现了,故不需要重复考虑。
枚举时要考虑两个方向的正方形。最后每个正方形枚举了4次。
关于哈希函数的选择,坐标的平方和产生的冲突较多,耗时较大,3000ms;用坐标和的话,922ms。
(关于点的检查,我也纠结了很久,就是新推出的点的哈希值是否会重合,或是它们怎么区分。最后,从大神们的博客找到答案,判断坐标是否一致。)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int mod = 2e5 + 3;
const int maxn = 1001;
int n;
int hash[mod];
int rec[maxn], cnt;
int next[maxn];
struct Node {
int x, y;
} node[maxn];
inline int Hash(int x, int y) {
return abs(x + y);
}
inline void hashit(int i) {
int p = Hash(node[i].x, node[i].y);
if (hash[p] == -1) {
hash[p] = i;
next[i] = -1;
}
else {
int j = hash[p];
next[i] = next[j];
next[j] = i;
}
}
inline bool check(int x, int y) {
int k = Hash(x, y);
int p = hash[k];
while (p != -1) {
if (node[p].x == x && node[p].y == y) return true;
p = next[p];
}
return false;
}
int main() {
while (~scanf("%d", &n) && n) {
for (int i = 0; i < n; i++) scanf("%d%d", &node[i].x, &node[i].y);
memset(hash, -1, sizeof(hash));
for (int i = 0; i < n; i++) hashit(i);
// for (int i = 0; i < n; i++) printf("%d%c", next[i], " \n"[i == n - 1]);
int ans = 0;
int x, y, t, k;
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++) {
// printf("i:%d, j:%d\n", i, j);
int x1, x2, y1, y2, dx, dy;
x1 = node[i].x; y1 = node[i].y;
x2 = node[j].x; y2 = node[j].y;
if (x1 > x2) {
int t = x1; x1 = x2; x2 = t;
t = y1; y1 = y2; y2 = t;
}
dy = abs(y1 - y2);
dx = x2 - x1;
int a1, b1, a2, b2;
a1 = x1 + dy; a2 = x2 + dy;
if (y1 >= y2) {
b1 = y1 + dx;
b2 = y2 + dx;
}
else {
b1 = y1 - dx;
b2 = y2 - dx;
}
if (check(a1, b1) && check(a2, b2)) ans++;
// printf("%d\n", ans);
a1 = x1 - dy; a2 = x2 - dy;
if (y1 >= y2) {
b1 = y1 - dx;
b2 = y2 - dx;
}
else {
b1 = y1 + dx;
b2 = y2 + dx;
}
if (check(a1, b1) && check(a2, b2)) ans++;
// printf("%d\n", ans);
}
printf("%d\n", ans >> 2);
}
return 0;
}