牛客网 2018校招真题 吉比特 直线上的点

Description

牛客网 2018校招真题 直线上的点

Solving Ideas

https://leetcode.com/problems/max-points-on-a-line/

https://leetcode.com/problems/max-points-on-a-line/discuss/47113/A-java-solution-with-notes

https://www.jianshu.com/p/317c676f9e00

基本思路:
因为两点确定一条直线,所以可以遍历所有直线,并在遍历的过程中,把已遍历的直线缓存起来,当遇到的相同直线时,就把这条直线上存在的点的个数累加,这样就可以求得经过某个点的某条直线上最多的点的个数。

如何遍历所有的直线?
两层循环遍历所有直线。假设有数组Point[] points,则

for (int i = 0; i < points.length; i++) {
	...
	for (int j = i + 1; j < points.length; j++) {
		...
	}
	...
}

外层循环:表示遍历经过points[i]这个点的所有可能的直线
内层循环:表示由points[i]points[j]这两点确定的直线
因为由(points[i], points[j])确定的直线 与 由(points[j], points[i])确定的直线是相同,和两点的顺序无关,为了避免重复计算,所以内层循环将j初始化为i + 1

如何判断直线是否相同?
对于二维的情况,如果两条直线斜率相等且都通过某一个定点,那么可以判定这两条直线相等。
例如平面内有点A、B、C,直线AB的斜率为k1,直线AC的斜率为k2,因为k1=k2且两直线都通过点A,所以AB和AC是一条直线,即A、B、C三点共线。

对于三维的情况,同样可以采用类似的方法来进行判定,只不过空间中用的是向量描述而不是斜率。
对于空间中两点 A ( x 1 , y 1 , z 1 ) A(x_1, y_1, z_1) A(x1,y1,z1) B ( x 2 , y 2 , z 2 ) B(x_2, y_2, z_2) B(x2,y2,z2),则AB的方向向量为 ( x 2 − x 1 , y 2 − y 1 , z 2 − z 1 ) (x_2-x_1, y_2-y_1, z_2-z_1) (x2x1,y2y1,z2z1),由两点式可得AB直线方程为:
x − x 1 x 2 − x 1 = y − y 1 y 2 − y 1 = z − z 1 z 2 − z 1 \frac{x-x_1}{x_2-x_1}=\frac{y-y_1}{y_2-y_1}=\frac{z-z_1}{z_2-z_1} x2x1xx1=y2y1yy1=z2z1zz1

对于点 A ( x 1 , y 1 , z 1 ) A(x_1, y_1, z_1) A(x1,y1,z1) C ( x 3 , y 3 , z 3 ) C(x_3, y_3, z_3) C(x3,y3,z3),则AC的方向向量为 ( x 3 − x 1 , y 3 − y 1 , z 3 − z 1 ) (x_3-x_1, y_3-y_1, z_3-z_1) (x3x1,y3y1,z3z1),AC直线方程为:
x − x 1 x 3 − x 1 = y − y 1 y 3 − y 1 = z − z 1 z 3 − z 1 \frac{x-x_1}{x_3-x_1}=\frac{y-y_1}{y_3-y_1}=\frac{z-z_1}{z_3-z_1} x3x1xx1=y3y1yy1=z3z1zz1

当AB的方向向量 ( x 2 − x 1 , y 2 − y 1 , z 2 − z 1 ) (x_2-x_1, y_2-y_1, z_2-z_1) (x2x1,y2y1,z2z1)与AC的方向向量 ( x 3 − x 1 , y 3 − y 1 , z 3 − z 1 ) (x_3-x_1, y_3-y_1, z_3-z_1) (x3x1,y3y1,z3z1)平行时,AB、AC共线,此时两条直线方程是相等的。

简单来说,对于空间中的两条直线,如果方向向量平行,且都通过某一个定点,即方向向量共线时,则可以判定这两条直线相等。

特殊情况处理:
遍历时,外层循环的点当做定点,当内层循环的点与该定点重叠时,则将其累加到该定点确定的所有直线的点数上。

Solution
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

class Point {
    int x;
    int y;
    int z;
    Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

/**
 * @author wylu
 */
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        Point[] points = new Point[n];
        for (int i = 0; i < n; i++) {
            String[] strs = br.readLine().split(" ");
            points[i] = new Point(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), Integer.parseInt(strs[2]));
        }
        System.out.println(maxPoints(points));
    }

    private static int maxPoints(Point[] points) {
        if (points.length <= 2) return points.length;

        Map<Integer, Map<Integer, Map <Integer, Integer>>> map = new HashMap<>();
        int res = 0;
        for (int i = 0; i < points.length; i++) {
            int overlap = 0, count = 0;
            for (int j = i + 1; j < points.length; j++) {
            	//此处不取绝对值是因为下方通过约分可以保证其转为正整数
                int dx = points[i].x - points[j].x;
                int dy = points[i].y - points[j].y;
                int dz = points[i].z - points[j].z;
                if (dx == 0 && dy == 0 && dz == 0) {
                    overlap++;
                    continue;
                }
				
				// (2,2,2)与(1,1,1)平行,在这里因为都通过points[i],所以(2,2,2)与(1,1,1)共线
                int gcd = gcd(dx, dy, dz);
                dx /= gcd;
                dy /= gcd;
                dz /= gcd;

                if (map.containsKey(dx)) {
                    if (map.get(dx).containsKey(dy)) {
                        if (map.get(dx).get(dy).containsKey(dz)) {
                            map.get(dx).get(dy).put(dz, map.get(dx).get(dy).get(dz) + 1);
                        } else {
                            map.get(dx).get(dy).put(dz, 1);
                        }
                    } else {
                        Map<Integer, Integer> m = new HashMap<>();
                        m.put(dz, 1);
                        map.get(dx).put(dy, m);
                    }
                } else {
                    Map<Integer, Integer> m1 = new HashMap<>();
                    m1.put(dz, 1);
                    Map<Integer, Map<Integer, Integer>> m2 = new HashMap<>();
                    m2.put(dy, m1);
                    map.put(dx, m2);
                }
                count = Math.max(count, map.get(dx).get(dy).get(dz));
            }
            res = Math.max(res, count + 1 + overlap);
            //如果下一个定点的直线的方向向量与当前定点的直线的方向向量平行时,
            //内层循环将会判定这两个方向向量共线,但实际上有可能并不共线,所以必须清除缓存
            map.clear();
        }
        return res;
    }

    private static int gcd(int a, int b, int c) {
        return gcd(a, gcd(b, c));
    }

    private static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值