一、题目描述
输入N个互不相同的二维整数坐标,求这N个坐标可以构成的正方形数量。输入的第一行为N,代表坐标数量,N为正整数且N≤100。之后的N行输入为坐标xy,以空格分隔,x和y均为整数,且-10≤x, y≤10。输出可以构成的正方形数量。
二、解题思路
要解决这个问题,可以使用以下思路:
-
遍历所有可能的点对:
- 使用两个嵌套的循环遍历所有可能的点对(i,j),其中i和j分别代表两个点的索引。
-
计算正方形的其他两个顶点:
- 对于每一对点(x1,y1)和(x2,y2),可以计算出另外两个可能的正方形顶点的坐标。
- 一种可能的计算方法是:
- x3 = x1 - (y1 - y2)
- y3 = y1 + (x1 - x2)
- x4 = x2 - (y1 - y2)
- y4 = y2 + (x1 - x2)
- 另一种可能的计算方法是:
- x5 = x1 + (y1 - y2)
- y5 = y1 - (x1 - x2)
- x6 = x2 + (y1 - y2)
- y6 = y2 - (x1 - x2)
- 一种可能的计算方法是:
- 对于每一对点(x1,y1)和(x2,y2),可以计算出另外两个可能的正方形顶点的坐标。
-
检查计算出的点是否在给定的点集中:
- 使用一个集合(或哈希表)来存储所有的点,以便快速查找。
- 检查计算出的点(x3,y3)、(x4,y4)、(x5,y5)和(x6,y6)是否都在点集中。
-
统计正方形的数量:
- 如果四个点都在点集中,则找到了一个正方形。
- 由于每个正方形可能被计算多次(例如,通过不同的点对计算得出),因此需要确保不重复计数。
- 一种简单的方法是,由于每个正方形被计算了4次(通过其4条边中的任意2条边都可以确定一个正方形),所以最后的结果需要除以4。
三、代码实现
import java.util.*;
public class SquareCounter {
/**
* 计算由给定点集能够组成的正方形的数量
*
* @param points 二维数组,每个元素表示平面上的一个点,包含x和y坐标
* @return 正方形的数量
*/
public static int countSquares(int[][] points) {
// 使用HashMap来存储点,以便快速查找,键为点的字符串表示
Map<String, Boolean> pointMap = new HashMap<>();
for (int i = 0; i < points.length; i++) {
int x = points[i][0];
int y = points[i][1];
String key = x + "," + y;
pointMap.put(key, true);
}
int count = 0;
int n = points.length;
// 枚举所有可能的点对
for (int i = 0; i < n; i++) {
int x1 = points[i][0];
int y1 = points[i][1];
for (int j = i + 1; j < n; j++) {
int x2 = points[j][0];
int y2 = points[j][1];
// 计算两组可能的正方形顶点
int x3 = x1 - (y1 - y2);
int y3 = y1 + (x1 - x2);
String key3 = x3 + "," + y3;
int x4 = x2 - (y1 - y2);
int y4 = y2 + (x1 - x2);
String key4 = x4 + "," + y4;
if (pointMap.containsKey(key3) && pointMap.containsKey(key4)) {
count++;
}
int x5 = x1 + (y1 - y2);
int y5 = y1 - (x1 - x2);
String key5 = x5 + "," + y5;
int x6 = x2 + (y1 - y2);
int y6 = y2 - (x1 - x2);
String key6 = x6 + "," + y6;
if (pointMap.containsKey(key5) && pointMap.containsKey(key6)) {
count++;
}
}
}
// 由于每个正方形被计算了4次(通过其4条边),所以需要除以4
return count / 4;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取点的数量
int n = scanner.nextInt();
int[][] points = new int[n][2];
// 读取点的坐标
for (int i = 0; i < n; i++) {
points[i][0] = scanner.nextInt();
points[i][1] = scanner.nextInt();
}
// 计算并输出结果
int result = countSquares(points);
System.out.println(result);
scanner.close();
}
}
四、注意事项
- 输入检查:确保输入的N为正整数,且坐标值在给定范围内。
- 性能优化:使用集合(或哈希表)来存储和查找点,以提高性能。
- 避免重复计数:由于每个正方形可能被计算多次,因此需要确保不重复计数。
输入:
5
0 0
1 1
1 0
0 1
2 2
解释:
- 输入的第一行是点的数量
n = 5
。 - 接下来的五行是五个点的坐标,分别是
(0, 0)
,(1, 1)
,(1, 0)
,(0, 1)
, 和(2, 2)
。
预期输出:
1
这表示在给定的点集中,可以构成一个正方形。
解释:
- 这五个点可以构成一个正方形,其顶点为
(0, 0)
,(1, 0)
,(1, 1)
, 和(0, 1)
。 - 注意,虽然
(2, 2)
点也在输入中,但它并不参与构成这个正方形。 - 由于每个正方形在这个算法中会被其四条边分别计算一次,所以最终的结果需要除以4。但在这个例子中,由于只有一个正方形,所以除以4后结果仍然是1。
运行解析
-
输入解析:
- 第一个数字
5
表示点的数量。 - 接下来的
10
个数字表示 5 个点的坐标,每两个数字表示一个点的(x, y)
坐标。 - 具体点的坐标为:
(0, 0)
,(1, 1)
,(1, 0)
,(0, 1)
,(2, 2)
。
- 第一个数字
-
算法逻辑:
- 首先,将点集存储在一个
HashMap
中,以便快速查找。键是点的字符串表示(x,y
),值是布尔值(这里使用true
,但实际上值不重要,只关心键是否存在)。 - 然后,通过两层循环枚举所有可能的点对
(x1, y1)
和(x2, y2)
。 - 对于每对点,计算两组可能的正方形顶点
(x3, y3)
和(x4, y4)
,以及(x5, y5)
和(x6, y6)
。 - 检查这两组顶点是否都在点集中。如果在,则意味着找到了一个正方形。
- 由于每个正方形通过其四条边会被计算四次,所以最终的结果需要除以 4。
- 首先,将点集存储在一个
-
具体计算:
- 对于点
(0, 0)
和(1, 1)
:- 计算
(x3, y3) = (-1, 1)
和(x4, y4) = (0, 2)
,这两个点不在点集中。 - 计算
(x5, y5) = (1, -1)
和(x6, y6) = (2, 0)
,这两个点也不在点集中。
- 计算
- 对于点
(0, 0)
和(1, 0)
:- 计算
(x3, y3) = (0, 0)
和(x4, y4) = (1, -1)
,点(0, 0)
在点集中,但(1, -1)
不在。 - 计算
(x5, y5) = (0, 0)
(重复,忽略)和(x6, y6) = (1, 1)
,这两个点都在点集中,但(0, 0)
是起始点,所以这里不形成新的正方形。
- 计算
- 对于点
(0, 0)
和(0, 1)
:- 计算
(x3, y3) = (1, 0)
和(x4, y4) = (0, -1)
,点(1, 0)
在点集中,但(0, -1)
不在。 - 计算
(x5, y5) = (-1, 0)
和(x6, y6) = (0, 1)
(重复,忽略),这里也不形成新的正方形。
- 计算
- 对于点
(0, 0)
和(2, 2)
:- 计算
(x3, y3) = (-2, 2)
和(x4, y4) = (0, 4)
,这两个点不在点集中。 - 计算
(x5, y5) = (2, -2)
和(x6, y6) = (4, 0)
,这两个点也不在点集中。
- 计算
- 类似地,检查其他点对,但在这个特定的点集中,只有一对点
(1, 0)
和(0, 1)
会形成正方形(1, 0)
,(0, 1)
,(0, 0)
,(1, 1)
。
- 对于点
-
结果:
- 在这个例子中,只有一个正方形,即
(0, 0)
,(1, 0)
,(1, 1)
,(0, 1)
。 - 由于每个正方形被计算了 4 次,所以初始计数为
4
,最终结果为4 / 4 = 1
。
- 在这个例子中,只有一个正方形,即
因此,对于输入 5 0 0 1 1 1 0 0 1 2 2
,输出结果为 1
。