n条线有多少个相交点?

时间:2019-11-15 22:18:55

标签: algorithm graph-algorithm

问题是,通过给出x0,y0,x1,y1给n行。获取集合点数。三点或更多线不相交。行通过(x0,y0),(x1,y1)。不会有同一行。 我有一个想法是先声明一个HashMap,当有新行出现时,得到它的斜坡。如果slop值不在HashMap中,则结果=结果+我们现在有多少行。如果该坡度位于HashMap中,则结果= result +(我们现在有多少行-此坡度有多少行)。 这是我的主要代码部分。

public static void main(String[] args){
    Scanner in = new Scanner(System.in);
    int[][] lines = new int[in.nextInt()][4];
    Map<Double, Integer> map = new HashMap<>();
    int cross = 0;
    for(int i = 0; i< lines.length; i++){
        for(int j = 0; j< 4; j++)
            lines[i][j] = in.nextInt();
        double slop;
        if(lines[i][2] - lines[i][0] == 0)
            slop = Double.POSITIVE_INFINITY;
        else
            slop = (double)(lines[i][3] - lines[i][1]) / (lines[i][2] - lines[i][0]);
        if(!map.containsKey(slop)) {
            cross = cross + i;
            map.put(slop, 1);
        }else{
            cross = cross + (i - map.get(slop));
            map.put(slop, map.get(slop) + 1);
        }
    }
    System.out.print(cross + "\n");
}

但是测试结果表明这是错误的。有人可以帮助我解决我没有注意到的情况,或者我的代码有什么问题吗。

2 个答案:

答案 0 :(得分:0)

任何一对不平行的线都将相交。

因此,交点数是唯一斜率数的多项式函数。

将直线的斜率指定为Rise/Run,但是直接使用该除法是危险的,因为在浮点数学中4.2/2.28.4/4.4可能不会得出相同的结果。同样,减去x0-x1可能会导致浮点怪异。

由于看起来好像您是以整数形式读取坐标,因此请考虑将斜率存储为(Rise,Run)对。但是请注意-Rise/RunRise/-Run看起来有所不同;因此,请约定,如果斜率为负,则该符号应存储在Rise中。现在,也许可以使用GCD algorithm简化分数。

现在,您的坡度有了独特而精确的形式(比以前更好了)。

现在,请像以前一样进行操作:如果斜率是唯一的,则斜率的数量会增加哈希集中已有条目的数量。

答案 1 :(得分:0)

这是一个很好的小问题。由于我们正在处理线,而且我们知道同一条线不会出现两次,并且我们知道同一条交点不会出现两次,因此解决方案几乎就像计数一个一样简单每对线的交点,即n * (n-1) / 2。但是,这假设每对线都有一个交点,如果有任何线是平行的,那是不正确的。因此我们需要计算每个斜率有多少条线共享同一斜率。看来您已经知道了。

但是,从代码中尚不清楚您是否正确使用了这些斜率计数来计算交点数。循环中存在一些加法和减法,这很难推理。如果在两个单独的阶段中执行该算法,则该算法将更加简洁。

如果有r条线具有相同的斜率,则n * (n-1) / 2公式假定它们将形成r * (r-1) / 2个交点,但是由于这些线是平行的,因此它们实际上在0个交点处他们。因此,我们需要减去r * (r-1) / 2来纠正计数。我们需要针对每个坡度独立进行此操作。

因此该算法可以按以下方式工作:

  • 建立坡度计数图。
  • total初始化为n * (n-1) / 2,其中n是行数。
  • 对于地图值中的每个计数r,从总数中减去r * (r-1) / 2

这是一个实现:

import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;

public class CountIntersections {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        long n = in.nextInt();

        Map<Double, Integer> slopeCounts = new HashMap<>();
        for(int i = 0; i < n; i++) {
            int x0 = in.nextInt(), y0 = in.nextInt(),
                x1 = in.nextInt(), y1 = in.nextInt();

            double slope;
            if(x0 == x1) {
                slope = Double.POSITIVE_INFINITY;
            } else {
                slope = (double) (y1 - y0) / (x1 - x0);
            }

            slopeCounts.put(slope, slopeCounts.getOrDefault(slope, 0) + 1);
        }

        long total = n * (n-1) / 2;
        for(long r : slopeCounts.values()) {
            total -= r * (r-1) / 2;
        }
        System.out.println(total);
    }
}

将变量ntotalr声明为类型long而不是int,这样乘法n * (n-1)就不会overflow。对于n和/或r的值大于约46,000的问题。

请注意,此处将斜率计算为double没有问题。从扫描仪读取的数字是int,所有数字都可以精确地表示为double。相减将是精确的,the IEEE 754 specification guarantees that division of floating point numbers will be correctly rounded。因此,由于浮点运算的原因,数值不精确度不会导致两个等斜率的计算略有不同。