算法每一题,成长每一天~
C0E30 构成正方形的数量
真题链接:【持续更新】2024华为 OD 机试E卷 机考真题库清单(全真题库)
思路
Java
package com.ccr.paper_f;
import javafx.util.Pair;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class C0E30 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
if (n < 4) {
System.out.println(0);
return;
}
Point[] points = new Point[n];
for (int i = 0; i < n; i++) {
points[i] = new Point(in.nextInt(), in.nextInt());
}
// 计算所有可能得到的正方形
List<Square> squares = new ArrayList<>(n * n / 2);
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
Pair<Square, Square> pair =
getTwoSquaresByTwoPoints(points[i], points[j]);
squares.add(pair.getKey());
squares.add(pair.getValue());
}
}
// 四个点都存在
Set<Point> inputPoints = Arrays.stream(points).collect(Collectors.toSet());
Predicate<Square> exists = square ->
inputPoints.contains(square.p1)
&& inputPoints.contains(square.p2)
&& inputPoints.contains(square.p3)
&& inputPoints.contains(square.p4);
long count = squares.stream()
.filter(exists) // 过滤出存在的正方形
.distinct() // 根据 hashCode + equals 去重
.count();
System.out.println(count);
}
/**
* 坐标点,也表示向量
*/
public static class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (this == obj) return true;
if (!(obj instanceof Point)) return false;
return x == ((Point) obj).x && y == ((Point) obj).y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
/**
* 正方形
*/
public static class Square {
// 四个坐标点,其中2两个已知
public final Point vector;
public final Point p1;
public final Point p2;
public final Point p3;
public final Point p4;
// 中心点,有小数,所以不用Point表示
public final String centerPoint;
public final double area; // 面积
/**
* 通过两个点,和垂直向量,得到一个正方形
*
* @param p1
* @param p2
*/
public Square(Point p1, Point p2, Point vector) {
this.p1 = p1;
this.p2 = p2;
this.vector = vector;
// 根据向量,得出另外两个点坐标
this.p3 = new Point(p1.x + vector.x, p1.y + vector.y);
this.p4 = new Point(p2.x + vector.x, p2.y + vector.y);
// 计算中心点、面积
this.centerPoint = (p1.x + p4.x) / (double) 2 + "_" + (p1.y + p4.y) / (double) 2; // p1与p4相对
this.area = Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (!(o instanceof Square)) return false;
Square square = (Square) o;
// 中心点、面积相同,且至少有一个点相同,则认为相等
return Objects.equals(centerPoint, square.centerPoint)
&& Objects.equals(area, square.area)
&& (
Objects.equals(p1, square.p1)
|| Objects.equals(p1, square.p2)
|| Objects.equals(p1, square.p3)
|| Objects.equals(p1, square.p4));
}
@Override
public int hashCode() {
return Objects.hash(centerPoint, area); // hashCode碰撞后,交给equals判断
}
}
/**
* 通过两个点,得到两个正方形
*
* @param p1
* @param p2
* @return
*/
public static Pair<Square, Square> getTwoSquaresByTwoPoints(Point p1, Point p2) {
Point vector1 = new Point(p1.y - p2.y, p2.x - p1.x); // 垂直向量
Point vector2 = new Point(-vector1.x, -vector1.y); // 反向垂直向量
return new Pair<>(new Square(p1, p2, vector1), new Square(p1, p2, vector2));
}
}
总结
1、这是最先想到的方法,前面分析的时间比较久,比较容易理解,但是代码量偏多。(后面看看如果有更好的方法,再更新 = . =)
2、关键是 构造 Point
、Square
两个类,定义它们的 equals()
、hashCode()
方法,用于校验重复。
xx:测试
7
0 0
1 2
3 1
2 -1
-1 3
-2 1
2 4
结果:3
算法要多练多练多练!!