繁体   English   中英

光盘序列中的相交数

[英]Number of intersections in a sequence of discs

我尝试从Codility解决一个问题,

我们在飞机上画了N张光盘。 光盘的编号从0到N −1。给出了由N个非负整数组成的数组A,用于指定光盘的半径。 绘制第J个圆盘,其中心位于(J,0),半径为A [J]。

我们说,如果J≠K,并且第J和第K光盘具有至少一个公共点(假设这些光盘包含边界),则第J光盘和第K光盘相交。

下图显示了N = 6和A绘制的光盘,如下所示:

  A[0] = 1
  A[1] = 5
  A[2] = 2
  A[3] = 1
  A[4] = 4
  A[5] = 0

有十一对(无序)的光盘相交,即:

光盘1和4相交,并且都与所有其他光盘相交; 光盘2也与光盘0和3相交。编写函数:

class Solution { public int solution(int[] A); }

给定如上所述的描述N个光盘的数组A,它返回(无序)对相交光盘的数量。 如果相交对的数量超过10,000,000,则函数应返回-1。

给定上面显示的数组A,函数应返回11,如上所述。

假使,假设:

N为[0..100,000]范围内的整数; 数组A的每个元素都是[0..2,147,483,647]范围内的整数。 复杂:

预期的最坏情况下的时间复杂度为O(N * log(N)); 预期的最坏情况下的空间复杂度为O(N)(不计算输入参数所需的存储空间)。

我也有一个我试图理解的解决方案,

public static int solution(int[] A) {

        int N = A.length;
        int[] sum = new int[N];

        for (int i = 0; i < N; i++) {

            int right;

            if (N - 1 >= A[i] + i) {
                right = i + A[i];
            } else {
                right = N - 1;
            }

            sum[right]++;
        }

        for (int i = 1; i < N; i++) {
            sum[i] += sum[i - 1];
        }

        int result = N * (N - 1) / 2;

        for (int j = 0; j < N; j++) {

            int left;

            if (j - A[j] < 0) {
                left = 0;
            } else {
                left = j - A[j];
            }

            if (left > 0) {
                result -= sum[left - 1];//.
            }
        }

        if (result > 10000000) {
            return -1;
        }

        return result;
    }

虽然我对解决方案有部分了解,但我无法完全理解它。 我在下面描述

一世。 我们创建了一个长度为N的数组sum ,并用光盘的最右边的点作为索引填充。 如果最右边的点大于N-1 ,我们将其设置为N-1

II。 执行数组sum的前缀和,即扫描数字x0,x1,x2,...是第二个数字y0,y1,y2,...的序列,前缀的总和(运行总数)为输入序列:

x0 = x0
x1 = x0 + x1
x2 = x0 + x1+ x2 

III。 计算提供的数组的最大可能交点为N * (N - 1) / 2

结果来自从2个元素的15种可能组合中进行选择,即C(N,R) 如果R = 2 ,我们可以推导C(N,R)= N!/ R!*(NR)! N * (N - 1) / 2

IV。 找到最左边的点,如果该值小于零,则将其设置为零。 然后,如果左值大于零,则将其转换为索引和结果中的内容。

首先,我无法理解最后一步。 谁能解释得更好? 我认为在最后一行result -= sum[left - 1]我们从最大可能值中减去对没有交集,现在我尝试理解计算

解决方案背后的想法是补码 ,它很容易计算相交的总数,也很容易获得未相交的对的总数。

然后,我们提供了您提供的解决方案,该解决方案包含四个步骤:

  1. 计算每个圆可以到达的圆的正确终点(我们只需要N-1最右边),这就是我们在第一个loop所做的;
  2. 从最左边开始到最后进行总结,用于确定第二个 loop不能相交的数量;
  3. 获取所有可能的对-这很容易,因为它只是组合;
  4. 检查每个圈中的第二环路和减去所有不能由当前通过圆相交的圆圈left = j - A[j]; (可以相邻但不能相交,这里的left是当前圆可以到达的最左边的点)。

  // count the rightmost point for each circle; for (int i = 0; i < N; i++) { int right; if (N - 1 >= A[i] + i) { right = i + A[i]; } else { right = N - 1; } sum[right]++; } // summing up since `i` cannot be reached/intersected from the left, there is no way the `right-er` can; for (int i = 1; i < N; i++) { sum[i] += sum[i - 1]; } // get the total amount of combinations; int result = N * (N - 1) / 2; for (int j = 0; j < N; j++) { int left; // the leftmost point the current circle can reach; if (j - A[j] < 0) { // avoid invalid; left = 0; } else { left = j - A[j]; } if (left > 0) { // if it's valid, sum[left-1] will be the un-intersected, subtract it; result -= sum[left - 1]; } } 

我为以下解决方案提供了解释,

  1. 在前缀sum之后, sum[i]存储最右边的点在0 to i (inclusive)的光盘数。 如果i = N-1 ,那么它将存储0 to i or higher (inclusive)光盘数

  2. 在最后一个循环中,left是光盘最左边的点的值,sum [left -1]是光盘最右边的点在0 to (left-1)的计数。 因此,对于该特定的光盘,不可能与这些光盘相交,因此我们需要从最大可能的交点中减去计数。

我们的目的是为特定的光盘找到不相交的光盘数量。 对于具有第j个索引的特定光盘,最左边的点将是j - A[j] ,对于所有以i为中心(可变)的光盘,如果i + A [i] <j-A [j ]就足够了。

就前缀数组而言,如果j是光盘的最左点,则它不会与sum[j-1]光盘总数相交。

如果最左边小于0,则将其设置为0;如果光盘最右边大于N-1 ,则将其设置为N-1 因为,如果最左边的点小于0,并且可以相交,则同一张光盘的最右边的点将位于=> 0(如果是点,则为=)。 因此,在比较光盘是否难处理时,将考虑使用该光盘。 当我们将最右边的点> N-1时,将最右边的点设置为N-1时,将应用类似的逻辑

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM