博主在面试字节跳动实习生岗位时,遇见了 n n n 多边形周长 k k k 等分的问题。其原题是所有边都为水平边或者垂直边。而这里博主给出更通用的版本——希望能对大家有所帮助。
简介
给定 n n n个点的坐标 f 1 ( x 1 , y 1 ) , f 2 ( x 2 , y 2 ) , … , f n ( x n , y n ) f_1(x_1, y_1), f_2(x_2, y_2), \dots, f_n(x_n, y_n) f1(x1,y1),f2(x2,y2),…,fn(xn,yn),它们共同组成了 n n n 边形。现在,我们需要将该 n n n 边形的周长进行 k k k 等分,并给出 k k k 个等分点的坐标。
思路
题目十分简单,我们只需要首先计算每条边的长度,进而求出周长和等分距离,再从某一点出发进行计算即可。当然,这里有一点需要格外注意。
假如我们有正方形,且顶点坐标分别为 ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , 1 ) , ( 0 , 1 ) (0, 0), (1, 0), (1, 1), (0, 1) (0,0),(1,0),(1,1),(0,1),并对它的周长进行3等分。不难发现,从点 ( 0 , 0 ) (0, 0) (0,0) 出发,其第一个等分点跨过了点 (1, 0),在点 (1, 0) 与点 (1, 1) 的连线上。则此时计算距离时,需要减去点 ( 0 , 0 ) (0, 0) (0,0) 至点 ( 1 , 0 ) (1, 0) (1,0) 的长度。
说到这,可能有的同学感到很抽象,不知从而入手。那么,我们就来看看源代码吧。
代码
import java.lang.Math;
import java.util.Scanner;
class Point{
double x;
double y;
/**
* 计算两点间的距离
* @param p1 点p1
* @param p2 点p2
* @return 距离
*/
public static double getDist(Point p1, Point p2){
return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
/**
* 获取等分点
* @param p1 点p1
* @param p2 点p2
* @param dist p1与p2的距离
* @param len 在p1与p2连线的长度
* @return 等分点
*/
public static Point getSplitPoint(Point p1, Point p2, double dist, double len){
Point splitPoint = new Point();
// 利用长度比例计算点的坐标
splitPoint.x = len / dist * (p1.x - p2.x) + p2.x;
splitPoint.y = len / dist * (p1.y - p2.y) + p2.y;
return splitPoint;
}
/**
* 获取所有等分点
* @param points 多边形的点的左边
* @param splitPoints 多边形的等分店的坐标
* @param k k等分
*/
public static Point[] splitPerimeter(Point[] points, Point[] splitPoints, int k){
int i = 0, j = 0, n = points.length;
double l = 0;
double[] dist = new double[n];
// 计算周长
for(; i < n - 1; i++){
dist[i] = getDist(points[i], points[i + 1]);
l += dist[i];
}
dist[i] = getDist(points[i], points[0]);
l += dist[i];
// 平均距离
double avg = l / k;
double sum_tmp = dist[0], tmp = 0;
for(i = 0, j = 0; i < k; i++){
while(j < n){
// 跨点
if(avg > sum_tmp){
j++;
sum_tmp += dist[j];
}
// 找到对应的边
else{
tmp = sum_tmp - avg;
// 最后一个点
if(j + 1 == n){
splitPoints[i] = getSplitPoint(points[j], points[0], dist[j], tmp);
}
else{
splitPoints[i] = getSplitPoint(points[j], points[j + 1], dist[j], tmp);
}
sum_tmp = tmp;
break;
}
}
}
return splitPoints;
}
public static void main(String[] args) {
// n边形,k等分
int n = 0, k = 0;
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
// 输入坐标
int i = 0;
Point[] points = new Point[n];
Point[] splitPoints = new Point[k];
for(; i < n; i++){
Point p = new Point();
p.x = sc.nextDouble();
p.y = sc.nextDouble();
points[i] = p;
}
sc.close();
// 等分
splitPoints = splitPerimeter(points, splitPoints, k);
// 输出
for(i = 0; i < k; i++)
System.out.println("split point: x = " + splitPoints[i].x + '\t' + "y = " + splitPoints[i].y);
}
}