【算法修炼】直线、平面问题

本文探讨了平面直角坐标系中点集合确定的直线数量问题,以及直线上的点数最大值的计算方法。通过举例展示了如何计算特定区域内整数点确定的直线总数,并提出了利用直线方程进行去重的策略。此外,还分析了平面被多条直线切分的情况,阐述了每增加一条直线对平面分段数的影响。最后,提到了一个消灭老鼠问题的实例,同样运用直线方程来寻找不同直线并去重。
摘要由CSDN通过智能技术生成

一、点集合确定的不同直线数

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。

给定平面上 2 × 3 个整点 {(x,y)|0 ≤ x < 2,0 ≤ y < 3, x ∈ Z,y ∈ Z},即横坐标是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数的点。这些点一共确定了 11 条不同的直线。

给定平面上 20 × 21 个整点 {(x,y)|0 ≤ x < 20,0 ≤ y < 21, x ∈ Z,y ∈ Z},即横坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

遍历两个点,采用直线方程:(y1-y2) * x +(x2-x1) * y +( x1 * y2 - x2 * y1)=0直线方程,最好采用这种方式,可以避免精度问题、分数问题,注意要求三个数的gcd,并约分,如果求直线条数可以直接装到set中。

上面的直线方程由两点式直线方程推出,即 (y - y2) / (y1 - y2) = (x - x2) / (x1 - x2)

二、直线上最多的点数

在这里插入图片描述
在这里插入图片描述
思路,遍历两个点,统计从每个点出发的k(斜率)的个数,只取出现次数最多的k,个数+1就 = 对当前点来说,一条直线最多能够经过的点的个数(这条直线一定包含当前点,这就是前面+1的原因),再从剩下的点出发,依次比较答案,取最大值即可。

class Solution {
    public int maxPoints(int[][] points) {
        int ans = 0;
        for (int i = 0; i < points.length; i++) {
            HashMap<String, Integer> map = new HashMap<>();
            // 对于当前点来说,一条直线最多经过的点的个数
            int max = 0;
            for (int j = i + 1; j < points.length; j++) {
                int x1 = points[i][0], y1 = points[i][1];
                int x2 = points[j][0], y2 = points[j][1];
                int up = y2 - y1;
                int down = x2 - x1;
                int tmp = gcd(up, down);
                up /= tmp;
                down /= tmp;
                String str = up + "_" + down;
                map.put(str, map.getOrDefault(str, 0) + 1);
                max = Math.max(max, map.get(str));
            }
            // 别忘了自己本身也算一个点
            ans = Math.max(ans, max + 1);
        }
        return ans;
    }
    public int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
}

三、平面切分

【问题描述】
平面上有 N 条直线,其中第 i 条直线是 y = Ai · x + Bi。
请计算这些直线将平面分成了几个部分。
【输入格式】
第一行包含一个整数 N。
以下 N 行,每行包含两个整数 Ai; Bi。
【输出格式】
一个整数代表答案。
【样例输入】
3
1 1
2 2
3 3
1
2
3
4
【样例输出】
6
1
【评测用例规模与约定】
对于 50% 的评测用例, 1 ≤ N ≤ 4, −10 ≤ Ai; Bi ≤ 10。
对于所有评测用例, 1 ≤ N ≤ 1000, −100000 ≤ Ai; Bi ≤ 100000。

平面中只有一条直线时(划分两个平面),如果增加一条平行的直线,划分的平面数 + 1;如果增加的一条直线是相交的,划分的平面数 + 2 = 4。再增加一条线与之前两条线相交,平面数 + 3 = 7。
规律:每增加一条直线,增加的平面数 = 该直线与之前直线的交点数 + 1

需要注意的是,可能有重复边的情况,要去重,这里选择直接对坐标点去重,更加方便。

题目中直接告诉了斜率和截距,那就很方便,不用再求k了。

y = A1X + B1,y = A2X + B2,交点,x = (B2 - B1) / (A1 - A2),y = A1X + B1或者y = A2X + B2都可以。

四、消灭老鼠

在这里插入图片描述

在这里插入图片描述
用(y1-y2) * x +(x2-x1) * y +( x1 * y2 - x2 * y1)=0直线方程来表示,找不同直线的条数,注意去重,注意使用gcd对三数约分。

import java.util.HashSet;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		// 激光枪所在位置
		int x2 = scan.nextInt();
		int y2 = scan.nextInt();
		int[] mouses = new int[n];
		// 计算当前点与与激光枪形成直线,求直线条数即可(去重)
		HashSet<String> set = new HashSet<>();
		// (y1 - y2) * x + (x2 - x1) * y + (x1 * y2 - x2 * y1) = 0
		for (int i = 0; i < n; i++) {
			int x1 = scan.nextInt();
			int y1 = scan.nextInt();
			int a = y1 - y2;
			int b = x2 - x1;
			int c = x1 * y2 - x2 * y1;
			// 三数约分
			int div = gcd(a, gcd(b, c));
			a /= div;
			b /= div;
			c /= div;
			String tmp = a + "x" + "+" + b + "y" + "+" + c;
			set.add(tmp);
		}
		System.out.println(set.size());
	}
	public static int gcd(int a, int b) {
		return b == 0 ? a : gcd(b, a % b);
	}
}

如果题目中要求站在原点,遍历不同的方向,那就需要把四个象限的k值分开考虑,此时最好的方法时, 求gcd时,都使用绝对值,这样得到的最大公约数都是正数,可以分别为每个象限记录k值。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值