【问题标题】:Number of intersections in a sequence of discs圆盘序列中的交叉点数
【发布时间】:2018-07-25 04:06:00
【问题描述】:

我尝试从 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

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

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

三。计算所提供数组的最大可能交集N * (N - 1) / 2

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

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

首先,我不明白最后一步发生了什么。谁能解释得更好?我认为在最后一行result -= sum[left - 1] 我们从最大可能中扣除了对没有交集,现在我尝试理解计算

【问题讨论】:

    标签: java arrays algorithm


    【解决方案1】:

    解决方案背后的想法是complement,很容易计算出交集的总数,也不难得到未交对的总数

    然后我们就有了你提供的解决方案,解决方案有四个步骤:

    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];
                }
         }

    【讨论】:

    • 详细说明会有很大帮助
    • 我理解解决方案,但你可以写更好的解释
    • 是的...我知道您的担忧并正在努力解释...请
    • 不,我也写一个答案。非常感谢。
    • 我的荣幸,@Arefe
    【解决方案2】:

    我对下面的解决方案进行了解释,

    1. 在前缀和之后,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 是圆盘的最左边点,则它不与sum[j-1] 圆盘的总数相交。

    如果最左边小于 0,我们将其设置为 0,如果光盘的最右边大于N-1,我们将其设置为N-1。因为,如果最左边的点小于 0,要相交,相同圆盘的最右边的点将在 => 0 中(= 在点的情况下)。所以在比较盘子不难处理的时候,会考虑这个盘子。当我们将最右边的点设置为 N-1,当它 > N-1

    时,类似的逻辑也适用

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-24
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      相关资源
      最近更新 更多