package lujing; import java.util.ArrayList; import java.util.List; public class Qufenxingzhuang { // 容差阈值:sin(角度)。 // 0.05 大约对应 2.8度。意味着小于3度的微小凹陷或凸起会被忽略,视为直线。 // 如果您觉得判断依然太严,可以将此值调大到 0.1 (约5.7度)。 private static final double ANGLE_TOLERANCE = 0.1; // 判断草地类型的主方法 public int judgeGrassType(String coordinates) { try { // 1. 解析坐标字符串 List points = parseCoordinates(coordinates); // 2. 数据预处理:删除最后一个闭合点 // 原因:输入数据通常首尾相同,删除最后一个点避免重复计算 if (!points.isEmpty()) { points.remove(points.size() - 1); } // 3. 检查点数(移除后至少需要3个点) if (points.size() < 3) { return 0; // 无法构成多边形 } // 4. 判断形状 if (isConvexPolygon(points)) { return 1; // 凸形草地 } else { return 2; // 异形草地(明显的凹多边形) } } catch (Exception e) { // 解析失败或计算异常 return 0; } } // 解析坐标字符串 private List parseCoordinates(String coordinates) { List points = new ArrayList<>(); String cleanStr = coordinates.replaceAll("[()\\[\\]{}]", "").trim(); String[] pointStrings = cleanStr.split(";"); for (String pointStr : pointStrings) { pointStr = pointStr.trim(); if (pointStr.isEmpty()) continue; String[] xy = pointStr.split(","); if (xy.length != 2) throw new IllegalArgumentException("格式错误"); points.add(new Point(Double.parseDouble(xy[0].trim()), Double.parseDouble(xy[1].trim()))); } return points; } // 【核心修改】判断多边形是否为凸多边形(带容差) private boolean isConvexPolygon(List points) { // 先清理极度接近的点,避免除以零错误 List cleanedPoints = cleanDuplicatePoints(points); int n = cleanedPoints.size(); if (n < 3) return false; boolean hasPositive = false; boolean hasNegative = false; for (int i = 0; i < n; i++) { Point p0 = cleanedPoints.get(i); Point p1 = cleanedPoints.get((i + 1) % n); Point p2 = cleanedPoints.get((i + 2) % n); // 计算向量 p0->p1 和 p1->p2 double dx1 = p1.x - p0.x; double dy1 = p1.y - p0.y; double dx2 = p2.x - p1.x; double dy2 = p2.y - p1.y; // 计算叉积 double cross = dx1 * dy2 - dx2 * dy1; // 计算边长 double len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); double len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); // 防止极短边导致的计算错误 if (len1 < 1e-6 || len2 < 1e-6) continue; // 【关键修改】计算归一化的叉积值 (相当于 sinθ) // 这代表了弯曲的程度,排除了边长对叉积数值大小的干扰 double sinTheta = cross / (len1 * len2); // 应用容差逻辑: // 只有当弯曲程度超过阈值时,才记录方向 if (sinTheta > ANGLE_TOLERANCE) { hasPositive = true; // 明显的左转(凸) } else if (sinTheta < -ANGLE_TOLERANCE) { hasNegative = true; // 明显的右转(凹) } // 如果 sinTheta 在 -0.05 到 0.05 之间,认为是直线抖动,忽略不计 // 如果既有明显的向左转,又有明显的向右转,那就是异形(凹多边形) if (hasPositive && hasNegative) { return false; } } // 如果没有产生冲突的转向,或者是单纯的直线(所有点共线),视为凸形 return true; } // 辅助:仅去除重复点,不再过度清理共线点(因为主逻辑已经能处理共线抖动) private List cleanDuplicatePoints(List points) { List cleaned = new ArrayList<>(); double tolerance = 1e-6; for (Point p : points) { if (!cleaned.isEmpty()) { Point last = cleaned.get(cleaned.size() - 1); if (Math.abs(p.x - last.x) < tolerance && Math.abs(p.y - last.y) < tolerance) { continue; // 跳过重复点 } } cleaned.add(p); } // 检查首尾是否因为循环导致重复(虽然主方法删除了最后一个点,为了健壮性再查一次) if (cleaned.size() > 1) { Point first = cleaned.get(0); Point last = cleaned.get(cleaned.size() - 1); if (Math.abs(first.x - last.x) < tolerance && Math.abs(first.y - last.y) < tolerance) { cleaned.remove(cleaned.size() - 1); } } return cleaned; } // 点类 private static class Point { double x, y; Point(double x, double y) { this.x = x; this.y = y; } } }