计算交叉盘数的算法

时间:2011-01-26 03:45:57

标签: algorithm language-agnostic

给定AN整数数组,我们在2D平面中绘制N个光盘,这样第i个光盘的中心位于(0,i)和半径{{ 1}}。如果第k个和第j个盘具有至少一个公共点,我们说第k个盘和第j个盘相交。

写一个函数

A[i]
如上所述,给出描述int number_of_disc_intersections(int[] A); 光盘的数组A

返回相交光盘对的数量。例如,给定N

N=6

有11对交叉光盘:

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

所以函数应该返回11。 如果交叉对的数量超过10,000,000,则该函数应返回-1。该函数可以假设0th and 1st 0th and 2nd 0th and 4th 1st and 2nd 1st and 3rd 1st and 4th 1st and 5th 2nd and 3rd 2nd and 4th 3rd and 4th 4th and 5th 不超过10,000,000。

43 个答案:

答案 0 :(得分:67)

O(N)复杂度和O(N)内存解决方案。

private static int Intersections(int[] a)
{
    int result = 0;
    int[] dps = new int[a.length];
    int[] dpe = new int[a.length];

    for (int i = 0, t = a.length - 1; i < a.length; i++)
    {
        int s = i > a[i]? i - a[i]: 0;
        int e = t - i > a[i]? i + a[i]: t;
        dps[s]++;
        dpe[e]++;
    }

    int t = 0;
    for (int i = 0; i < a.length; i++)
    {
        if (dps[i] > 0)
        {
            result += t * dps[i];
            result += dps[i] * (dps[i] - 1) / 2;
            if (10000000 < result) return -1;
            t += dps[i];
        }
        t -= dpe[i];
    }

    return result;
}

答案 1 :(得分:60)

因此,您希望找到间隔[i-A[i], i+A[i]]的交叉点数。

维护一个包含i-A[i]的排序数组(称之为X)(还有一些额外的空格,其中包含值i+A[i])。

现在从最左边的间隔(即最小i-A[i])开始走数组X.

对于当前间隔,进行二分搜索以查看间隔的右端点(即i+A[i])将去向何处(称为等级)。现在您知道它与左侧的所有元素相交。

增加一个具有等级的计数器并减去当前位置(假设一个被索引),因为我们不想重复计算间隔和自交叉。

O(nlogn)时间, O(n)空间。

答案 2 :(得分:12)

好吧,我将FalkHüffner的想法改编为c ++,并改变了范围。 与上面所写的相反,没有必要超出数组的范围(无论数值有多大)。 在Codility上,这段代码收到了100%。 谢谢Falk你的好主意!

int number_of_disc_intersections ( const vector<int> &A ) {
    int sum=0;
    vector<int> start(A.size(),0);
    vector<int> end(A.size(),0);
    for (unsigned int i=0;i<A.size();i++){
        if ((int)i<A[i]) start[0]++;
        else        start[i-A[i]]++;
        if (i+A[i]>=A.size())   end[A.size()-1]++;
        else                    end[i+A[i]]++;
    }
    int active=0;
    for (unsigned int i=0;i<A.size();i++){
        sum+=active*start[i]+(start[i]*(start[i]-1))/2;
        if (sum>10000000) return -1;
        active+=start[i]-end[i];
    }
    return sum;
}

答案 3 :(得分:10)

这甚至可以在线性时间内完成。实际上,如果忽略每个点上只有一个区间,并将其视为一组间隔的起点和终点这一事实,就会变得更容易。然后,您可以从左侧扫描它(Python代码为了简单起见):

from collections import defaultdict

a = [1, 5, 2, 1, 4, 0]

start = defaultdict(int)
stop = defaultdict(int)

for i in range(len(a)):
    start[i - a[i]] += 1
    stop[i + a[i]] += 1

active = 0
intersections = 0
for i in range(-len(a), len(a)):
    intersections += active * start[i] + (start[i] * (start[i] - 1)) / 2    
    active += start[i]
    active -= stop[i]

print intersections

答案 4 :(得分:10)

这是一个 O(N)时间, O(N)空间算法需要在整个数组中运行3次并且没有排序,verified scoring 100%

你对成对的光盘感兴趣。每对涉及一个盘的一侧和另一盘的另一侧。因此,如果我们处理每张光盘的一面,我们就不会有重复对。让我们左右两边调用(我在思考它的同时旋转了空间)。

重叠是由于右侧直接在中心处重叠另一个光盘(因此对半径等于半径而非常关心阵列长度),或者是由于最左边存在左侧的数量。

所以我们创建一个数组,其中包含每个点的左边数,然后它就是一个简单的总和。

C代码:

int solution(int A[], int N) {
    int C[N];
    int a, S=0, t=0;

    // Mark left and middle of disks
    for (int i=0; i<N; i++) {
        C[i] = -1;
        a = A[i];
        if (a>=i) {
            C[0]++;
        } else {
            C[i-a]++;
        }
    }
    // Sum of left side of disks at location
    for (int i=0; i<N; i++) {
        t += C[i];
        C[i] = t;
    }
    // Count pairs, right side only:
    // 1. overlaps based on disk size
    // 2. overlaps based on disks but not centers
    for (int i=0; i<N; i++) {
        a = A[i];
        S += ((a<N-i) ? a: N-i-1);
        if (i != N-1) {
          S += C[((a<N-i) ? i+a: N-1)];
        }
        if (S>10000000) return -1;
    }
    return S;
}

答案 5 :(得分:8)

Python 100/100(已测试)关于编码,具有O(nlogn)时间和O(n)空间。

这是@noisyboiler的@Aryabhatta方法的python实现,带有注释和示例。 完全归功于原作者,任何错误/不良措辞完全是我的错。

from bisect import bisect_right

def number_of_disc_intersections(A):
    pairs = 0

    # create an array of tuples, each containing the start and end indices of a disk
    # some indices may be less than 0 or greater than len(A), this is fine!
    # sort the array by the first entry of each tuple: the disk start indices
    intervals = sorted( [(i-A[i], i+A[i]) for i in range(len(A))] )

    # create an array of starting indices using tuples in intervals
    starts = [i[0] for i in intervals]

    # for each disk in order of the *starting* position of the disk, not the centre
    for i in range(len(starts)):

        # find the end position of that disk from the array of tuples
        disk_end = intervals[i][1]

        # find the index of the rightmost value less than or equal to the interval-end
        # this finds the number of disks that have started before disk i ends
        count = bisect_right(starts, disk_end )

        # subtract current position to exclude previous matches
        # this bit seemed 'magic' to me, so I think of it like this...
        # for disk i, i disks that start to the left have already been dealt with
        # subtract i from count to prevent double counting
        # subtract one more to prevent counting the disk itsself
        count -= (i+1)
        pairs += count
        if pairs > 10000000:
            return -1
    return pairs

工作示例:给定[3,0,1,6],磁盘半径如下所示:

disk0  -------         start= -3, end= 3
disk1      .           start=  1, end= 1
disk2      ---         start=  1, end= 3
disk3  -------------   start= -3, end= 9
index  3210123456789   (digits left of zero are -ve)

intervals = [(-3, 3), (-3, 9), (1, 1), (1,3)]
starts    = [-3, -3, 1, 1]

the loop order will be: disk0, disk3, disk1, disk2

0th loop: 
    by the end of disk0, 4 disks have started 
    one of which is disk0 itself 
    none of which could have already been counted
    so add 3
1st loop: 
    by the end of disk3, 4 disks have started 
    one of which is disk3 itself
    one of which has already started to the left so is either counted OR would not overlap
    so add 2
2nd loop: 
    by the end of disk1, 4 disks have started 
    one of which is disk1 itself
    two of which have already started to the left so are either counted OR would not overlap
    so add 1
3rd loop: 
    by the end of disk2, 4 disks have started
    one of which is disk2 itself
    two of which have already started to the left so are either counted OR would not overlap
    so add 0

pairs = 6
to check: these are (0,1), (0,2), (0,2), (1,2), (1,3), (2,3),    

答案 6 :(得分:5)

我用这个C ++实现得到100分中的100分:

#include <map>
#include <algorithm>

inline bool mySortFunction(pair<int,int> p1, pair<int,int> p2)
{
    return ( p1.first < p2.first );
}

int number_of_disc_intersections ( const vector<int> &A ) {
    int i, size = A.size();
    if ( size <= 1 ) return 0;
    // Compute lower boundary of all discs and sort them in ascending order
    vector< pair<int,int> > lowBounds(size);
    for(i=0; i<size; i++) lowBounds[i] = pair<int,int>(i-A[i],i+A[i]);
    sort(lowBounds.begin(), lowBounds.end(), mySortFunction);
    // Browse discs
    int nbIntersect = 0;
    for(i=0; i<size; i++)
    {
        int curBound = lowBounds[i].second;
        for(int j=i+1; j<size && lowBounds[j].first<=curBound; j++)
        {
            nbIntersect++;
            // Maximal number of intersections
            if ( nbIntersect > 10000000 ) return -1;
        }
    }
    return nbIntersect;
}

答案 7 :(得分:2)

这是我的JavaScript解决方案,基于该线程中的其他解决方案,但以其他语言实现。

function solution(A) {
    let circleEndpoints = [];

    for(const [index, num] of Object.entries(A)) {
        circleEndpoints.push([parseInt(index)-num, true]);
        circleEndpoints.push([parseInt(index)+num, false]);
    }

    circleEndpoints = circleEndpoints.sort(([a, openA], [b, openB]) => {
        if(a == b) return openA ? -1 : 1;
        return a - b;
    });

    let openCircles = 0;
    let intersections = 0;

    for(const [endpoint, opening] of circleEndpoints) {
        if(opening) {
            intersections += openCircles;
            openCircles ++;
        } else {
            openCircles --;
        }
        if(intersections > 10000000) return -1;
    }

    return intersections;
}

答案 8 :(得分:2)

Python回答

from bisect import bisect_right

def number_of_disc_intersections(li):
    pairs = 0
    # treat as a series of intervals on the y axis at x=0
    intervals = sorted( [(i-li[i], i+li[i]) for i in range(len(li))] )
    # do this by creating a list of start points of each interval
    starts = [i[0] for i in intervals]
    for i in range(len(starts)):
        # find the index of the rightmost value less than or equal to the interval-end
        count = bisect_right(starts, intervals[i][1])
        # subtract current position to exclude previous matches, and subtract self
        count -= (i+1)
        pairs += count
        if pairs > 10000000:
            return -1
    return pairs

答案 9 :(得分:2)

Java 2 * 100%。

result被宣布为一个案例,但是一个案件的密集性没有进行测试,即在一个点上有50k * 50k的交叉点。

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

        int[] westEnding = new int[A.length];
        int[] eastEnding = new int[A.length];

        for (int i=0; i<A.length; i++) {
            if (i-A[i]>=0) eastEnding[i-A[i]]++; else eastEnding[0]++;
            if ((long)i+A[i]<A.length) westEnding[i+A[i]]++; else westEnding[A.length-1]++;
        }

        long result = 0; //long to contain the case of 50k*50k. codility doesn't test for this.
        int wests = 0;
        int easts = 0;
        for (int i=0; i<A.length; i++) {

            int balance = easts*wests; //these are calculated elsewhere

            wests++;
            easts+=eastEnding[i];

            result += (long) easts*wests - balance - 1; // 1 stands for the self-intersection
            if (result>10000000) return -1;

            easts--;
            wests-= westEnding[i];
        }

        return (int) result;
    }
}

答案 10 :(得分:1)

我提供另一种解决方案,因为我没有发现之前解决方案的计数原理易于遵循。虽然结果相同,但似乎值得展示一个解释和更直观的计数过程。

首先,首先考虑 O(N^2) 解决方案,该解决方案按照圆盘的中心点的顺序迭代圆盘,并计算与当前圆盘相交的以当前圆盘右侧为中心的圆盘数量,使用条件 current_center + radius >= other_center - radius。请注意,我们可以使用条件 current_center - radius <= other_center + radius 获得相同的结果,计算以当前圆盘左侧为中心的圆盘。

def simple(A):
    """O(N^2) solution for validating more efficient solution."""
    N = len(A)
    unique_intersections = 0
    # Iterate over discs in order of their center positions 
    for j in range(N):
        # Iterate over discs whose center is to the right, to avoid double-counting.
        for k in range(j+1, N):
            # Increment cases where edge of current disk is at or right of the left edge of another disk.
            if j + A[j] >= k - A[k]:
                unique_intersections += 1
                # Stop early if we have enough intersections.
                # BUT: if the discs are small we still N^2 compare them all and time out.
                if unique_intersections > 10000000:
                    return -1
    return unique_intersections

如果我们只能“查找”与当前圆盘相交的右侧(或左侧!)圆盘的数量,我们可以从 O(N^2) 到 O(N)。关键的见解是将相交条件重新解释为“一个圆盘的右边缘与另一个圆盘的左边缘重叠”,这意味着(哈哈!)中心无关紧要,只有边缘。

下一个见解是尝试对边进行排序,花费 O(N log N) 时间。给定左边缘的排序数组和右边缘的排序数组,当我们沿着数轴从左到右扫描时,当前位置点左侧的左边缘或右边缘的数量就是当前分别索引到 left_edges 和 right_edges:恒定时间推导。

最后,我们使用“右边缘>左边缘”条件来推导出当前磁盘与仅从当前磁盘左侧开始的磁盘之间的交叉点数(以避免重复) 是当前边左侧的左侧边数减去当前边左侧的右侧边数。也就是说,从这个开始左边的盘数减去已经关闭的盘数。

现在对于这段代码,在 Codility 上进行了 100% 的测试:

def solution(A):
    """O(N log N) due to sorting, with O(N) pass over sorted arrays"""
    N = len(A)
    # Left edges of the discs, in increasing order of position.
    left_edges = sorted([(p-r) for (p,r) in enumerate(A)])
    # Right edges of the discs, in increasing order of position. 
    right_edges = sorted([(p+r) for (p,r) in enumerate(A)])
    #print("left edges:", left_edges[:10])
    #print("right edges:", right_edges[:10])
    intersections = 0
    right_i = 0
    # Iterate over the discs in order of their leftmost edge position.
    for left_i in range(N):
        # Find the first right_edge that's right of or equal to the current left_edge, naively:
        # right_i = bisect.bisect_left(right_edges, left_edges[left_i])
        # Just scan from previous index until right edge is at or beyond current left:
        while right_edges[right_i] < left_edges[left_i]:
             right_i += 1
        # Count number of discs starting left of current, minus the ones that already closed.
        intersections += left_i - right_i
        # Return early if we find more than 10 million intersections.
        if intersections > 10000000:
            return -1
    #print("correct:", simple(A))
    return intersections

答案 11 :(得分:1)

count = 0
for (int i = 0; i < N; i++) {
  for (int j = i+1; j < N; j++) {
    if (i + A[i] >= j - A[j]) count++;
  }
}

O(N^2)这么慢,但它确实有效。

答案 12 :(得分:1)

我知道这是一个古老的问题,但它在编码方面仍然很活跃。

    private int solution(int[] A)
    {
        int openedCircles = 0;
        int intersectCount = 0;

我们需要带有其开始和结束值的圆。为此,我使用了Tuple。 正确/错误表示我们要添加“圆环起点”还是“圆环终点”值。

        List<Tuple<decimal, bool>> circles = new List<Tuple<decimal, bool>>();
        for(int i = 0; i < A.Length; i ++)
        {
            // Circle start value
            circles.Add(new Tuple<decimal, bool>((decimal)i - (decimal)A[i], true));

            // Circle end value
            circles.Add(new Tuple<decimal, bool>((decimal)i + (decimal)A[i], false));
        }

按其值对“圆圈”进行排序。 如果一个圆在另一个圆的起点处以相同的值结束,则应将其视为相交(因为如果在同一点,则“开口”应位于“闭合”的前面)

        circles = circles.OrderBy(x => x.Item1).ThenByDescending(x => x.Item2).ToList();

计数和返回计数器

        foreach (var circle in circles)
        {
            // We are opening new circle (within existing circles)
            if(circle.Item2 == true)
            {
                intersectCount += openedCircles;

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

                openedCircles++;
            }
            else
            {
                // We are closing circle
                openedCircles--;
            }
        }

        return intersectCount;
    }

答案 13 :(得分:1)

可能非常快。上)。但是您需要检查一下。 100%的Codility。 大意: 1.在表格的任何一点,直到圆圈的右边缘都有“打开”的圆圈数,让我们说“ o”。 2.因此,该点的圆存在(对o-1个)可能的对。 “已用”是指已处理的圆并已计算成对。

  public int solution(int[] A) { 
    final int N = A.length;
    final int M = N + 2;
    int[] left  = new int[M]; // values of nb of "left"  edges of the circles in that point
    int[] sleft = new int[M]; // prefix sum of left[]
    int il, ir;               // index of the "left" and of the "right" edge of the circle

    for (int i = 0; i < N; i++) { // counting left edges
      il = tl(i, A);
      left[il]++;
    }

    sleft[0] = left[0];
    for (int i = 1; i < M; i++) {// counting prefix sums for future use
      sleft[i]=sleft[i-1]+left[i];
    }
    int o, pairs, total_p = 0, total_used=0;
    for (int i = 0; i < N; i++) { // counting pairs
      ir = tr(i, A, M);
      o  = sleft[ir];                // nb of open till right edge
      pairs  = o -1 - total_used;
      total_used++;
      total_p += pairs;
    }
    if(total_p > 10000000){
      total_p = -1;
    }
    return total_p;
  }

    private int tl(int i, int[] A){
    int tl = i - A[i]; // index of "begin" of the circle
      if (tl < 0) {
        tl = 0;
      } else {
        tl = i - A[i] + 1;
      }
    return tl;
  }
  int tr(int i, int[] A, int M){
    int tr;           // index of "end" of the circle
      if (Integer.MAX_VALUE - i < A[i] || i + A[i] >= M - 1) {
        tr = M - 1;
      } else {
        tr = i + A[i] + 1;
      }
      return tr;
  }

答案 14 :(得分:1)

100/100 c#

  class Solution
    {
        class Interval
        {
            public long Left;
            public long Right;
        }

        public int solution(int[] A)
        {
            if (A == null || A.Length < 1)
            {
                return 0;
            }
            var itervals = new Interval[A.Length];
            for (int i = 0; i < A.Length; i++)
            {
                // use long to avoid data overflow (eg. int.MaxValue + 1)
                long radius = A[i];
                itervals[i] = new Interval()
                {
                    Left = i - radius,
                    Right = i + radius
                };
            }
            itervals = itervals.OrderBy(i => i.Left).ToArray();

            int result = 0;
            for (int i = 0; i < itervals.Length; i++)
            {
                var right = itervals[i].Right;
                for (int j = i + 1; j < itervals.Length && itervals[j].Left <= right; j++)
                {
                    result++;
                    if (result > 10000000)
                    {
                        return -1;
                    }
                }
            }
            return result;
        }
    }

答案 15 :(得分:1)

这是一种红宝石解决方案,在编码方面得分为100/100。我现在正在发布它,因为我发现很难按照已经发布的ruby回答。

def solution(a)
    end_points = []
    a.each_with_index do |ai, i|
        end_points << [i - ai, i + ai]
    end
    end_points = end_points.sort_by { |points| points[0]}

    intersecting_pairs = 0
    end_points.each_with_index do |point, index|
        lep, hep = point
        pairs = bsearch(end_points, index, end_points.size - 1, hep)
        return -1 if 10000000 - pairs + index < intersecting_pairs
        intersecting_pairs += (pairs - index)
    end
    return intersecting_pairs
end

# This method returns the maximally appropriate position
# where the higher end-point may have been inserted.
def bsearch(a, l, u, x)
    if l == u
        if x >= a[u][0]
            return u
        else
            return l - 1 
        end
    end
    mid = (l + u)/2

    # Notice that we are searching in higher range
    # even if we have found equality.
    if a[mid][0] <= x
        return bsearch(a, mid+1, u, x)
    else
        return bsearch(a, l, mid, x)
    end
end

答案 16 :(得分:0)

这是我在Python中的干净解决方案(无需导入库)。这种逻辑可以适应任何其他编程语言。

我的Codility为100%-时间:O(Nlog(N))-空间:O(N)

希望对您有帮助。

def solution(A):
  start, end = [], []
  for i, val in enumerate(A):
    start.append(i-val)
    end.append(i+val)

  start.sort()
  end.sort()

  count, currCircles, aux1, aux2 = 0, 0, 0, 0
  while aux1 != len(start) and aux2 != len(end):
    if aux1 < len(start) and start[aux1] <= end[aux2]:
      count += currCircles
      currCircles +=1
      aux1 +=1
      if currCircles > 10000000:
          return -1
    else:
      currCircles -=1
      aux2 +=1

  return count

答案 17 :(得分:0)

所以,我在 Scala 中进行了这个测试,我想在这里分享我的例子。我要解决的想法是:

提取数组上每个位置左右两侧的界限。

A[0] = 1 --> (0-1, 0+1) = A0(-1, 1)
A[1] = 5 --> (1-5, 1+5) = A1(-4, 6)
A[2] = 2 --> (2-2, 2+2) = A2(0, 4)
A[3] = 1 --> (3-1, 3+1) = A3(2, 4)
A[4] = 4 --> (4-4, 4+4) = A4(0, 8)
A[5] = 0 --> (5-0, 5+0) = A5(5, 5)

检查任意两个位置是否有交点

(A0_0 >= A1_0 AND A0_0 <= A1_1) OR // intersection
(A0_1 >= A1_0 AND A0_1 <= A1_1) OR // intersection
(A0_0 <= A1_0 AND A0_1 >= A1_1)    // one circle contain inside the other

如果这两项检查中的任何一项为真,则计算一个交叉点。

object NumberOfDiscIntersections {
  def solution(a: Array[Int]): Int = {
    var count: Long = 0
    for (posI: Long <- 0L until a.size) {
      for (posJ <- (posI + 1) until a.size) {
        val tupleI = (posI - a(posI.toInt), posI + a(posI.toInt))
        val tupleJ = (posJ - a(posJ.toInt), posJ + a(posJ.toInt))
        if ((tupleI._1 >= tupleJ._1 && tupleI._1 <= tupleJ._2) ||
          (tupleI._2 >= tupleJ._1 && tupleI._2 <= tupleJ._2) ||
          (tupleI._1 <= tupleJ._1 && tupleI._2 >= tupleJ._2)) {
          count += 1
        }
      }
    }
    count.toInt
  }
}

答案 18 :(得分:0)

这就是我所做的...它提供100%的性能和93%的正确性(因为一项测试“算术溢出测试”失败了。

public static int NumberOfDiscIntersections(int[] A)
    {
        int[] leftEdges = new int[A.Length];
        int[] rightEdges = new int[A.Length];
        int openDisc = 0;
        int intersect = 0;
        int leftEdgeIndex = 0;
        for (int i = 0; i < A.Length; i++)
        {
            leftEdges[i] = i - A[i];
            rightEdges[i] = i + A[i];
        }
        Array.Sort(leftEdges);
        Array.Sort(rightEdges);

        for (int j = 0; j < rightEdges.Length; j++)
        {
            for (int i = leftEdgeIndex; i < leftEdges.Length; i++)
            {
                if (leftEdges[i] <= rightEdges[j])
                {
                    intersect += openDisc;
                    openDisc += 1;
                    leftEdgeIndex += 1;
                }
                else
                {
                    openDisc -= 1;
                    break;
                }
                if (intersect > 10000000) return -1;
            }
            if(leftEdgeIndex == leftEdges.Length)
            {
                break; //All Left Edges are opened, So all Intersect calculated
            }                
        }
        return intersect;
    }

答案 19 :(得分:0)

花费O(N * log(N))时间和O(N)空间的Javascript解决方案。

<input type="datetime-local"  class="form-control" value="{{ $new_date }}">

答案 20 :(得分:0)

这是O(N)时间O(N)空间的解决方案。 无需排序。 该解决方案的Codility得分为100%。

function solution(A) {
  // write your code in JavaScript (Node.js 8.9.4)
  let totalUniquePairs = 0;
  
  if (A.length === 0) return 0;
  
  // array contain counts of how many circles begin at that index
  const opens = new Array(A.length).fill(0);
  // array containg counts of how many circles stop at that index
  const closes = new Array(A.length).fill(0);
  
  // populate the open/closes
  for (let i = 0; i < A.length; ++i) {
      const radius = A[i];
      // keep this within the bounds of the array
      const opensAt = Math.max(0, i - radius);
      const closesAt = Math.min(A.length - 1, i + radius);
      ++closes[closesAt];
      ++opens[opensAt];
  }

  
  // operate in sort of a stack fashion
  let overlapping = 0;
  for (let i = 0; i < A.length; ++i) {
      const opening = opens[i];
      const closing = closes[i];
      overlapping += opening;
      
      if (closing <= 0) continue; // no closing, no new pairs

      // naive way
      // for (let j = 0; j < closing; ++j) {
      //     // overlapping - 1 = possible unique pairs here
      //     totalUniquePairs += overlapping - 1;
      //     if (totalUniquePairs > 10000000) return -1;
      //     overlapping -= 1;
      // }
      
      // optimized way
      // summation pattern from 1 to some number k
      // closes = 3 => totalUniquePairs += (overlapping - 1) + (overlapping - 2) + (overlapping - 3);
      // ^ can be simplified as totalUniquePairs += (overlapping * k) - (k * (k + 1) / 2);
      // https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF
      totalUniquePairs += (overlapping * closing) - (closing * (closing + 1) / 2);
      if (totalUniquePairs > 10000000) return -1;
      overlapping -= closing;
  }
  
  
  return totalUniquePairs;
}

这种算法的工作方式是通过在要处理的索引处跟踪您正在处理的相交圆的数量。

当闭合圆时,您正在跟踪,您可以计算闭合圆和相交圆形成的唯一对的数量。唯一对的数量是当前相交圆的数量-1。然后,U从正在索引处跟踪的相交圆的数量中删除该圆,然后可以对在该索引处闭合的每个圆重复此模式。

这类似于在需要处理括号时弹出需要在堆栈中处理的方程式。

所以我们只需要为两件事花费内存:

  • 在这个索引i上有多少个圈子开放?打开数组即可处理
  • 在这个索引i上有多少个圈子开放?关闭数组处理此

答案 21 :(得分:0)

与其他答案(C ++)类似,尽管略有不同,但简短且解释清楚。时空都是线性的,而codility则是100%。

我们首先定义以下规则:

  1. 如果在点X上打开N张光盘(即N张光盘从索引X的最左端开始),则所有这些光盘相互交叉。我们不能计算双相交(盘1相交2,而盘2相交1 = 1相交,而不是2)。因此,计算相交的公式为:N(N-1)/ 2(易于手动检查)。
  2. 在X + 1点,您将打开另一组M光盘。我们使用点1的公式,并且由于我们没有关闭任何先前的光盘,因此我们添加了M * N个交点(也易于手动检查)。
  3. 如果在X + 1处,我们关闭了C个光盘,那么点2的公式将为M *(N-C),其中N-C是在X + 1点已经打开的光盘数量(不计)新的-M)。

因此,在这段代码中,我们首先要确定在每个位置(0A.size() - 1)上开始和结束的光盘数量。然后,我们通过以上公式更新交点。为了正确计算点3的公式,更新openDiscs计数器很重要。

    int solution(vector<int> &A) {
        // count start/end points at each position
        std::vector<size_t> starts(A.size(), 0);
        std::vector<size_t> ends(A.size(), 0);
        for (size_t idx = 0; idx < A.size(); ++idx)
        {
            // make sure not to have overflow here
            size_t s = std::max<int>(0, idx - A[idx]);
            size_t e = std::min<size_t>(A.size() - 1, idx + A[idx]);
        
            ++starts[s];
            ++ends[e];
        }
        // starts[idx] is a counter which indicates how many discs have their uttermost left point at idx
        // ends[idx] is a counter which indicates how many discs have their uttermost right point at idx

        // loop over lines and count intersections (make sure no overflow)
        unsigned long int openDiscs = 0;
        unsigned long int intersections = 0;
        for (size_t idx = 0; idx < A.size(); ++idx)
        {
            // intersections due to newly opened discs (point 1)
            intersections += starts[idx] * (starts[idx] - 1) / 2;
        
            // intersections due to previously opened discs (point 2, 3)
            intersections += openDiscs * starts[idx];
        
            // update open discs
            openDiscs += starts[idx] - ends[idx];
        
            if (intersections > 10000000) return -1;
        }
    
        return intersections;
    }

答案 22 :(得分:0)

如果将这些光盘画在纸上,则可以直观地假定它是按每个光盘的左端排序的。对于每张光盘,请查看右端在其他光盘上的延伸量,即-与其他光盘的左端相交并在此处停止检查。这样的停止节省了我们在O(n * n)最琐碎的解决方案上花费的时间,而节省了类似O(n * log(n))的时间。效率不如这里的某些O(n)解决方案高,但是它是一个简单易懂的代码,对于100%的Codility提交足够有效。解决此问题时,我确实从noisyboilerGnomeDePlume

的帖子中汲取了灵感

语言是Java语言

function solution(A) {

    //for each disk, create an array of objects containing left and right ends, then sort it by the left end. 
    const B = A.map((a, i) => ({ left: i - a, right: i + a })).sort((a, b) => a.left - b.left)
    let count = 0
    //index i is the disk we are testing intersection with any disc j
    for (let i = 0; i < B.length - 1; i++) {
        for (let j = i + 1; j < B.length; j++) {
            if (B[i].right >= B[j].left) {
                count++;
                //this is just an explicit condition for the codility task
                if (count > 10000000) return -1
            }
            else break;
            //since the array is sorted by the left ends, we know that we won't find any disk that the right side of disc i would reach
        }
    }
    return count
}

答案 23 :(得分:0)

基于此视频https://www.youtube.com/watch?v=HV8tzIiidSw的Javascript解决方案100/100

function sortArray(A) {
    return A.sort((a, b) => a - b)
}

function getDiskPoints(A) {
    const diskStarPoint = []
    const diskEndPoint = []
    for(i = 0; i < A.length; i++) {
        diskStarPoint.push(i - A[i])
        diskEndPoint.push(i + A[i])
    }
    return {
        diskStarPoint: sortArray(diskStarPoint),
        diskEndPoint: sortArray(diskEndPoint)
    };
}

function solution(A) {
    const { diskStarPoint, diskEndPoint } = getDiskPoints(A)
    let index = 0;
    let openDisks = 0;
    let intersections = 0;
    for(i = 0; i < diskStarPoint.length; i++) {
      while(diskStarPoint[i] > diskEndPoint[index]) {
        openDisks--
        index++
      }
      intersections += openDisks
      openDisks++
    }
    return intersections > 10000000 ? -1 : intersections
}

答案 24 :(得分:0)

golang solution,因为codility仅支持v1.4以上的版本,因此我们必须手动实现Sorter接口才能对2元组数组进行排序;

package solution

// you can also use imports, for example:
// import "fmt"
import "sort"

// you can write to stdout for debugging purposes, e.g.
// fmt.Println("this is a debug message")

type Circle struct {
    left, right int
}

type CircleSorter []*Circle

func (cs CircleSorter) Len() int {
    return len(cs)
}

func (cs CircleSorter) Swap(i, j int) {
    cs[i], cs[j] = cs[j], cs[i]
}

func (cs CircleSorter) Less(i, j int) bool {
    return cs[i].left < cs[j].left
}

func BiSecRight(data []*Circle, target int) int {
    return sort.Search(len(data), func(i int) bool {
        return data[i].left > target
    })
}

func Solution(A []int) int {
    data := make([]*Circle, len(A))
    for i := 0; i < len(A); i++ {
        data[i] = &Circle{
            left:  i - A[i],
            right: i + A[i],
        }
    }

    sort.Sort(CircleSorter(data))

    count := 0

    for i, v := range data {
        count += BiSecRight(data, v.right) - 1 - i
    }

    if count > 10000000 {
        count = -1
    }

    return count
}

答案 25 :(得分:0)

这在c#中得到100/100

class CodilityDemo3
{

    public static int GetIntersections(int[] A)
    {
        if (A == null)
        {
            return 0;
        }

        int size = A.Length;

        if (size <= 1)
        {
            return 0;
        }

        List<Line> lines = new List<Line>();

        for (int i = 0; i < size; i++)
        {
            if (A[i] >= 0)
            {
                lines.Add(new Line(i - A[i], i + A[i]));
            }
        }

        lines.Sort(Line.CompareLines);
        size = lines.Count;
        int intersects = 0;

        for (int i = 0; i < size; i++)
        {
            Line ln1 = lines[i];
            for (int j = i + 1; j < size; j++)
            {
                Line ln2 = lines[j];
                if (ln2.YStart <= ln1.YEnd)
                {
                    intersects += 1;
                    if (intersects > 10000000)
                    {
                        return -1;
                    }
                }
                else
                {
                    break;
                }
            }
        }

        return intersects;
    }

}

public class Line
{
    public Line(double ystart, double yend)
    {
        YStart = ystart;
        YEnd = yend;
    }
    public double YStart { get; set; }
    public double YEnd { get; set; }

    public static int CompareLines(Line line1, Line line2)
    {
        return (line1.YStart.CompareTo(line2.YStart));

    }
}

}

答案 26 :(得分:0)

感谢Falk的好主意!这是一个利用稀疏性的ruby实现。

def int(a)

    event = Hash.new{|h,k| h[k] = {:start => 0, :stop => 0}}
    a.each_index {|i|
        event[i - a[i]][:start] += 1
        event[i + a[i]][:stop ] += 1
    }
    sorted_events = (event.sort_by {|index, value| index}).map! {|n| n[1]}

    past_start = 0
    intersect = 0

    sorted_events.each {|e|

        intersect += e[:start] * (e[:start]-1) / 2 +
                     e[:start] * past_start

        past_start += e[:start]
        past_start -= e[:stop]
    }
    return intersect
end

puts int [1,1]

puts int [1,5,2,1,4,0]

答案 27 :(得分:0)

O(N * logN)100%/ 100%

// you can also use imports, for example:
import java.util.*;

// you can write to stdout for debugging purposes, e.g.
// System.out.println("this is a debug message");

class Solution {
    public int solution(int[] A) {
        long[] startPositionList = new long[A.length];
        long[] finishPositionList = new long[A.length];
        for (int i = 0; i < A.length; i++) {
            // (long), because otherwise number 2,147,483,647 will overflow
            startPositionList[i] = (long)i - A[i];
            finishPositionList[i] = (long)i + A[i];
        }

        Arrays.sort(startPositionList);
        Arrays.sort(finishPositionList);

        int count = 0;
        int multiplier = 0;
        int startIndex = 0;
        int finishIndex = 0;
        // After we passed through all start positions, our solution won't change, hence we can stop the loop
        while (startIndex < A.length) {
            if (startPositionList[startIndex] > finishPositionList[finishIndex]) {
                // Finish position is next smallest
                multiplier--;
                finishIndex++;
            } else {
                // Start position is next smallest or is equal to finish position
                count += multiplier;
                multiplier++;
                startIndex++;
                // There is a condition to return -1 if count is bigger than 1e7
                if (count > (int)1e7) {
                    return -1;
                }
            }
        }

        return count;
    }
}

答案 28 :(得分:0)

 const endpoints = [];
    A.map((val, index) => {
        endpoints.push([index - val, 'S']);
        endpoints.push([index + val, 'E']);
    });
    endpoints.sort((a, b) => {
        if (a[0] < b[0]) {
            return -1;
        }
        if (a[0] > b[0]) {
            return 1;
        }
        if (a[0] === b[0] && a[1] === 'S')
            return -1;
        else
            return 1;
    });

    let count = 0;
    let intersections = 0;
    let point = [];
    const length = endpoints.length;
    for (let i = 0; i < length; i++) {
        point = endpoints[i];
        if (point[1] === 'S') {
            count += intersections;
            intersections += 1
        } else {
            intersections -= 1;
        }
        if (intersections > 10e6)
            return -1;
    }
    return count;

答案 29 :(得分:0)

Aryabhatta(二进制搜索解决方案)所描述的100/100 C#实现。

using System;

class Solution {
    public int solution(int[] A)
    {
        return IntersectingDiscs.Execute(A);
    }
}

class IntersectingDiscs
{
    public static int Execute(int[] data)
    {
        int counter = 0;

        var intervals = Interval.GetIntervals(data);

        Array.Sort(intervals); // sort by Left value

        for (int i = 0; i < intervals.Length; i++)
        {
            counter += GetCoverage(intervals, i);

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

        return counter;
    }

    private static int GetCoverage(Interval[] intervals, int i)
    {
        var currentInterval = intervals[i];

        // search for an interval starting at currentInterval.Right

        int j = Array.BinarySearch(intervals, new Interval { Left = currentInterval.Right });

        if(j < 0)
        {
            // item not found

            j = ~j; // bitwise complement (see Array.BinarySearch documentation)

            // now j == index of the next item larger than the searched one

            j = j - 1; // set index to the previous element
        }

        while(j + 1 < intervals.Length && intervals[j].Left == intervals[j + 1].Left)
        {
            j++; // get the rightmost interval starting from currentInterval.Righ
        }

        return j - i; // reduce already processed intervals (the left side from currentInterval)
    }
}

class Interval : IComparable
{
    public long Left { get; set; }
    public long Right { get; set; }

    // Implementation of IComparable interface
    // which is used by Array.Sort().
    public int CompareTo(object obj)
    {
        // elements will be sorted by Left value

        var another = obj as Interval;

        if (this.Left < another.Left)
        {
            return -1;
        }

        if (this.Left > another.Left)
        {
            return 1;
        }

        return 0;
    }

    /// <summary>
    /// Transform array items into Intervals (eg. {1, 2, 4} -> {[-1,1], [-1,3], [-2,6]}).
    /// </summary>
    public static Interval[] GetIntervals(int[] data)
    {
        var intervals = new Interval[data.Length];

        for (int i = 0; i < data.Length; i++)
        {
            // use long to avoid data overflow (eg. int.MaxValue + 1)

            long radius = data[i];

            intervals[i] = new Interval
            {
                Left = i - radius,
                Right = i + radius
            };
        }

        return intervals;
    }
}

答案 30 :(得分:0)

以下是@Aryabhatta在科特林接受的答案的实现,因此所有功劳归@Aryabhatta

fun calculateDiscIntersections(A: Array<Int>): Int {
    val MAX_PAIRS_ALLOWED = 10_000_000L
    //calculate startX and endX for each disc
    //as y is always 0 so we don't care about it. We only need X
    val ranges = Array(A.size) { i ->
        calculateXRange(i, A[i])
    }

    //sort Xranges by the startX
    ranges.sortBy { range ->
        range.start
    }

    val starts = Array(ranges.size) {index ->
        ranges[index].start
    }

    var count = 0
    for (i in 0 until ranges.size) {
        val checkRange = ranges[i]

        //find the right most disc whose start is less than or equal to end of current disc
        val index = bisectRight(starts, checkRange.endInclusive, i)

        //the number of discs covered by this disc are:
        //count(the next disc/range ... to the last disc/range covered by given disc/range)
        //example: given disc index = 3, last covered (by given disc) disc index = 5
        //count = 5 - 3 = 2
        //because there are only 2 discs covered by given disc
        // (immediate next disc with index 4 and last covered disc at index 5)
        val intersections = (index - i)

        //because we are only considering discs intersecting/covered by a given disc to the right side
        //and ignore any discs that are intersecting on left (because previous discs have already counted those
        // when checking for their right intersects) so this calculation avoids any duplications
        count += intersections

        if (count > MAX_PAIRS_ALLOWED) {
            return -1
        }
    }

    return if (count > MAX_PAIRS_ALLOWED) {
        -1
    } else {
        count
    }
}

private fun calculateXRange(x: Int, r: Int): LongRange {
    val minX = x - r.toLong()
    val maxX = x + r.toLong()

    return LongRange(minX, maxX)
}

fun bisectRight(array: Array<Long>, key: Long, arrayStart: Int = 0): Int {
    var start = arrayStart
    var end = array.size - 1
    var bisect = start

    while (start <= end) {
        val mid = Math.ceil((start + end) / 2.0).toInt()
        val midValue = array[mid]
        val indexAfterMid = mid + 1

        if (key >= midValue) {
            bisect = mid
        }

        if (key >= midValue && (indexAfterMid > end || key < array[indexAfterMid])) {
            break
        } else if (key < midValue) {
            end = mid - 1
        } else {
            start = mid + 1
        }
    }

    return bisect
}

Codility Solution得分为100%。

答案 31 :(得分:0)

这里已经有很多不错的答案,包括已接受答案的出色解释。但是,我想指出一点关于Python语言中实现细节的观察。

最初,我提出了以下所示的解决方案。我原本希望在有O(N*log(N))个迭代的单个for循环时获得N的时间复杂度,并且每个迭代执行一次最多需要log(N)的二进制搜索。

def solution(a):
    import bisect
    if len(a) <= 1:
        return 0
    cuts = [(c - r, c + r) for c, r in enumerate(a)]
    cuts.sort(key=lambda pair: pair[0])
    lefts, rights = zip(*cuts)
    n = len(cuts)
    total = 0
    for i in range(n):
        r = rights[i]
        pos = bisect.bisect_right(lefts[i+1:], r)
        total += pos
        if total > 10e6:
            return -1
    return total

但是,我得到O(N**2)和超时失败。你看到这里有什么问题吗?正确,此行:

pos = bisect.bisect_right(lefts[i+1:], r)

在这一行中,您实际上是在获取原始列表的一份副本,以将其传递给二进制搜索功能,这完全破坏了所提出的解决方案的效率!它使您的代码更加简洁(即,您无需编写pos - i - 1),但是却严重影响了性能。因此,如上所示,解决方案应为:

def solution(a):
    import bisect
    if len(a) <= 1:
        return 0
    cuts = [(c - r, c + r) for c, r in enumerate(a)]
    cuts.sort(key=lambda pair: pair[0])
    lefts, rights = zip(*cuts)
    n = len(cuts)
    total = 0
    for i in range(n):
        r = rights[i]
        pos = bisect.bisect_right(lefts, r)
        total += (pos - i - 1)
        if total > 10e6:
            return -1
    return total

似乎有时人们可能会急于制作切片和副本,因为Python允许您如此轻松地完成它:)可能不是一个很好的见解,但对我来说,这是一个很好的课程,更多地关注这些“技术性的”,将想法和算法转化为实际解决方案的时刻。

答案 32 :(得分:0)

以下是关于codility得分为100的PHP代码:

$sum=0;

//One way of cloning the A:
$start = array();
$end = array();

foreach ($A as $key=>$value)
{
$start[]=0;
$end[]=0;   
}  

for ($i=0; $i<count($A); $i++)
{
  if ($i<$A[$i]) 
  $start[0]++;
  else        
  $start[$i-$A[$i]]++;

  if ($i+$A[$i] >= count($A))   
  $end[count($A)-1]++;
  else
  $end[$i+$A[$i]]++;
}
$active=0;
for ($i=0; $i<count($A);$i++)
{
  $sum += $active*$start[$i]+($start[$i]*($start[$i]-1))/2;
  if ($sum>10000000) return -1;
  $active += $start[$i]-$end[$i];
}
return $sum;

但是我不理解逻辑。这只是从上面转换的C ++代码。伙计们,你能详细说明你在这做什么吗?

答案 33 :(得分:0)

C# 100/100 ,时间复杂度O(N*log(N)),空间复杂度O(N)

主要观点:

  1. 制作2个排序数组:左点和右点。
  2. 将这些数组合并到一个布尔数组中,其中true表示&#34;打开&#34;而false表示&#34;关闭&#34;间隔。
  3. 运行布尔数组并计算打开的间隔,将它们相加。
  4. _

    public int solution(int[] A) 
    {        
        var sortedStartPoints = A.Select((value, index) => (long)index-value).OrderBy(i => i).ToArray();
        var sortedEndPoints = A.Select((value, index) => (long)index+value).OrderBy(i => i).ToArray();     
    
        // true - increment, false - decrement, null - stop
        var points = new bool?[2*A.Length];
    
        // merge arrays
        for(int s=0, e=0, p=0; p < points.Length && s < sortedStartPoints.Length; p++)
        {
            long startPoint = sortedStartPoints[s];
            long endPoint = sortedEndPoints[e];
            if(startPoint <= endPoint)
            {
                points[p] = true;
                s++;
            }
            else
            {
                points[p] = false;
                e++;
            }
        }
    
        int result = 0;
        int opened = 0;
        // calculate intersections
        foreach(bool? p in points.TakeWhile(_ => _.HasValue))
        {
            if(result > 10000000)
                return -1;
    
            if(p == true)
            {
                result += opened;
                opened++;
            }
            else
            {                
                opened--;
            }
        }
    
        return result;
    }
    

答案 34 :(得分:0)

红宝石溶液。得分100%。

def solution(a)
  # write your code in Ruby 2.2
  open = Hash.new
  close = Hash.new

  (0..(a.length-1)).each do |c|
    r = a[c]
    open[ c-r ]  ? open[ c-r ]+=1  : open[ c-r ]=1
    close[ c+r ] ? close[ c+r ]+=1 : close[ c+r ]=1 
  end

  open_now = 0
  intersections = 0
  open.merge(close).keys.sort.each do |v|
    intersections += (open[v]||0)*open_now
    open_now += (open[v]||0) - (close[v]||0)
    if(open[v]||0)>1
      # sum the intersections of only newly open discs
      intersections += (open[v]*(open[v]-1))/2
      return -1 if intersections > 10000000
    end
  end
  intersections
end

答案 35 :(得分:0)

C#解决方案100/100

using System.Linq;

class Solution
{
    private struct Interval
    {
        public Interval(long @from, long to)
        {
            From = @from;
            To = to;
        }

        public long From { get; }
        public long To { get; }
    }

    public int solution(int[] A)
    {
        int result = 0;

        Interval[] intervals = A.Select((value, i) =>
        {
            long iL = i;
            return new Interval(iL - value, iL + value);
        })
        .OrderBy(x => x.From)
        .ToArray();

        for (int i = 0; i < intervals.Length; i++)
        {
            for (int j = i + 1; j < intervals.Length && intervals[j].From <= intervals[i].To; j++)
                result++;

            if (result > 10000000)
                return -1;
        }

        return result;
    }
}

答案 36 :(得分:0)

我在斯威夫特的回答;获得100%的分数。

import Glibc

struct Interval {
    let start: Int
    let end: Int
}

func bisectRight(intervals: [Interval], end: Int) -> Int {
    var pos = -1
    var startpos = 0
    var endpos = intervals.count - 1

    if intervals.count == 1 {
        if intervals[0].start < end {
            return 1
        } else {
            return 0
        }
    }

    while true {
        let currentLength = endpos - startpos

        if currentLength == 1 {
            pos = startpos
            pos += 1
            if intervals[pos].start <= end {
                pos += 1
            }
            break
        } else {
            let middle = Int(ceil( Double((endpos - startpos)) / 2.0 ))
            let middlepos = startpos + middle

            if intervals[middlepos].start <= end {
                startpos = middlepos
            } else {
                endpos = middlepos
            }
        }
    }

    return pos
}

public func solution(inout A: [Int]) -> Int {
    let N = A.count
    var nIntersections = 0

    // Create array of intervals
    var unsortedIntervals: [Interval] = []
    for i in 0 ..< N {
        let interval = Interval(start: i-A[i], end: i+A[i])
        unsortedIntervals.append(interval)
    }

    // Sort array
    let intervals = unsortedIntervals.sort {
        $0.start < $1.start
    }

    for i in 0 ..< intervals.count {
        let end = intervals[i].end

        var count = bisectRight(intervals, end: end)

        count -= (i + 1)
        nIntersections += count

        if nIntersections > Int(10E6) {
            return -1
        }
    }

    return nIntersections
}

答案 37 :(得分:0)

#include <stdio.h>
#include <stdlib.h>
void sortPairs(int bounds[], int len){
    int i,j, temp;
    for(i=0;i<(len-1);i++){
        for(j=i+1;j<len;j++){
            if(bounds[i] > bounds[j]){
                temp = bounds[i];
                bounds[i] = bounds[j];
                bounds[j] = temp;
                temp = bounds[i+len];
                bounds[i+len] = bounds[j+len];
                bounds[j+len] = temp;
            }
        }
    }
}
int adjacentPointPairsCount(int a[], int len){
    int count=0,i,j;
    int *bounds;
    if(len<2) {
        goto toend;
    }
    bounds = malloc(sizeof(int)*len *2);
    for(i=0; i< len; i++){
        bounds[i] = i-a[i];
        bounds[i+len] = i+a[i];
    }
    sortPairs(bounds, len);
    for(i=0;i<len;i++){
        int currentBound = bounds[i+len];
        for(j=i+1;a[j]<=currentBound;j++){
            if(count>100000){
                count=-1;
                goto toend;
            }
            count++;
        }     
    }
toend:
    free(bounds);
    return count;   
}

答案 38 :(得分:0)

这是一个两遍C ++解决方案,不需要任何库,二进制搜索,排序等。

int solution(vector<int> &A) {
    #define countmax 10000000
    int count = 0;
    // init lower edge array
    vector<int> E(A.size());
    for (int i = 0; i < (int) E.size(); i++)
        E[i] = 0;
    // first pass
    // count all lower numbered discs inside this one
    // mark lower edge of each disc
    for (int i = 0; i < (int) A.size(); i++)
    {
        // if disc overlaps zero
        if (i - A[i] <= 0)
            count += i;
        // doesn't overlap zero
        else {   
            count += A[i];
            E[i - A[i]]++;
        }
        if (count > countmax)
            return -1;
    }
    // second pass
    // count higher numbered discs with edge inside this one
    for (int i = 0; i < (int) A.size(); i++)
    {
        // loop up inside this disc until top of vector
        int jend = ((int) E.size() < (long long) i + A[i] + 1 ? 
                    (int) E.size() : i + A[i] + 1);
        // count all discs with edge inside this disc
        // note: if higher disc is so big that edge is at or below 
        // this disc center, would count intersection in first pass
        for (int j = i + 1; j < jend; j++)
            count += E[j];
        if (count > countmax)
            return -1;
    }
    return count;
}

答案 39 :(得分:0)

Codility中的100%得分。

以下是对Толя solution的C#的修改:

    public int solution(int[] A)
    {
        long result = 0;
        Dictionary<long, int> dps = new Dictionary<long, int>();
        Dictionary<long, int> dpe = new Dictionary<long, int>();

        for (int i = 0; i < A.Length; i++)
        {
            Inc(dps, Math.Max(0, i - A[i]));
            Inc(dpe, Math.Min(A.Length - 1, i + A[i]));
        }

        long t = 0;
        for (int i = 0; i < A.Length; i++)
        {
            int value;
            if (dps.TryGetValue(i, out value))
            {
                result += t * value;
                result += value * (value - 1) / 2;
                t += value;
                if (result > 10000000)
                    return -1;
            }
            dpe.TryGetValue(i, out value);
            t -= value;
        }

        return (int)result;
    }

    private static void Inc(Dictionary<long, int> values, long index)
    {
        int value;
        values.TryGetValue(index, out value);
        values[index] = ++value;
    }

答案 40 :(得分:0)

以上Java中所述的实现:

public class DiscIntersectionCount {


public int number_of_disc_intersections(int[] A) {
    int[] leftPoints = new int[A.length];
    for (int i = 0; i < A.length; i++) {
        leftPoints[i] = i - A[i];
    }

    Arrays.sort(leftPoints);
//      System.out.println(Arrays.toString(leftPoints));
    int count  = 0;
    for (int i = 0; i < A.length - 1; i++) {
        int rpoint = A[i] + i;

        int rrank = getRank(leftPoints, rpoint);

        //if disk has sifnificant radius, exclude own self
        if (rpoint > i) rrank -= 1;
        int rank = rrank; 
//          System.out.println(rpoint+" : "+rank);

        rank -= i;
        count += rank;
    }
    return count;

}

public int getRank(int A[], int num) {
    if (A==null || A.length == 0) return -1;        
    int mid = A.length/2;
    while ((mid >= 0) && (mid < A.length)) {

        if (A[mid] == num) return mid;

        if ((mid == 0) && (A[mid] > num)) return -1;
        if ((mid == (A.length - 1)) && (A[mid] < num)) return A.length;
        if (A[mid] < num && A[mid + 1] >= num) return mid + 1;
        if (A[mid] > num && A[mid - 1] <= num) return mid - 1;

        if (A[mid] < num) mid = (mid + A.length)/2;
        else  mid = (mid)/2;

    }

    return -1;
}

public static void main(String[] args) {
    DiscIntersectionCount d = new DiscIntersectionCount();
    int[] A = 
        //{1,5,2,1,4,0}
        //{0,0,0,0,0,0}
    //  {1,1,2}
    {3}
     ;
    int count = d.number_of_disc_intersections(A);
    System.out.println(count);
}
}

答案 41 :(得分:-1)

JavaScript 2016

Aryabhattas的JavaScript版本。我已经改变了一点,使它更具JS和更高效的性能,并添加了注释来解释算法的作用。希望这会有所帮助。

function solution(A) {
  var result = 0,
    len = A.length,
    dps = new Array(len).fill(0),
    dpe = new Array(len).fill(0),
    i,
    active = len - 1,
    s,
    e;

  for (i = 0; i < len; i++) {

    // adds to the begin array how many discs begin at the specific position given
    if (i > A[i])
      s = i - A[i];
    else
      s = 0;

    // adds to the end array the amount of discs that end at this specific position
    if (active - i > A[i])
      e = i + A[i]
    else
      e = active;

    // a disc always begins and ends somewhere, s and e are the starting and ending positions where this specific disc for the element in A at position i reside
    dps[s] += 1;
    dpe[e] += 1;
  }

  // no discs are active as the algorithm must calculate the overlaps first, then the discs can be made active, hence why it begins with 0 active
  active = 0;
  for (i = 0; i < len; i++) {
    if (dps[i] > 0) {
      // new discs has entered the zone, multiply it with the current active discs as the new discs now overlap with the older active ones
      result += active * dps[i];
      // new discs must also be counted against each other and not just the ones which were active prior
      result += dps[i] * (dps[i] - 1) / 2;
      // assignment rules
      if (10000000 < result)
        return -1;
      // add new discs to the active list that have started at this position
      active += dps[i];
    }
    // remove discs as they have ended at this position
    active -= dpe[i];
  }
  // return the result
  return result;
}

var A = [1, 5, 2, 1, 4, 0]; // should return 11
console.log(solution(A));

答案 42 :(得分:-1)

下面的Java语言解决方案将获得100/100

if (A == null || A.length < 2) {
  return 0;
}

int[] B = Arrays.copyOf(A, A.length);
Arrays.sort(B);
int biggest = B[A.length - 1];
int intersections = 0;
for (int i = 0; i < A.length; i++) {
  for (int j = i + 1; j < A.length; j++) {
    if (j - biggest > i + A[i]) {
      break;
    }
    if (j - A[j] <= i + A[i]) {
      intersections++;
    }
    if (intersections > 10000000) {
      return -1;
    }
  }
}

return intersections;