多边形周长等分

博主在面试字节跳动实习生岗位时,遇见了 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);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值