Подтвердить что ты не робот

Алгоритм вычисления числа пересекающихся дисков

Учитывая массив A из N целых чисел, мы рисуем диски N в 2D-плоскости, так что i-й диск имеет центр в (0,i) и радиус A[i]. Мы говорим, что k-й диск и j-й диск пересекаются, если k-й и j-й диски имеют хотя бы одну общую точку.

Напишите функцию

int number_of_disc_intersections(int[] A);

который задал массив A, описывающий диски N, как описано выше, возвращает число пар пересекающихся дисков. Например, с учетом N=6 и

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

имеется 11 пар пересекающихся дисков:

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

поэтому функция должна возвращать 11. Функция должна возвращать -1, если число пересекающихся пар превышает 10 000 000. Функция может предполагать, что N не превышает 10 000 000.

4b9b3361

Ответ 1

Итак, вы хотите найти число пересечений интервалов [i-A[i], i+A[i]].

Поддерживать отсортированный массив (называть его X), содержащий i-A[i] (также есть дополнительное пространство, в котором есть значение i+A[i]).

Теперь пройдите массив X, начиная с самого левого интервала (наименьший i-A[i]).

Для текущего интервала выполните двоичный поиск, чтобы увидеть, куда будет идти правая конечная точка интервала (т.е. i+A[i]) (называемая рангом). Теперь вы знаете, что он пересекает все элементы слева.

Прибавьте счетчик с рангом и вычтите текущую позицию (при условии, что она проиндексирована), поскольку мы не хотим удваивать интервалы подсчета и самопересечения.

O (nlogn) время, O (n) пространство.

Ответ 2

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

Ответ 3

Хорошо, я адаптировал идею Фалька Хюффнера к С++ и внес изменения в диапазон. Напротив того, что написано выше, нет необходимости выходить за рамки массива (независимо от того, насколько велики значения в нем). На 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;
}

Ответ 4

Это можно сделать даже в линейном времени. Фактически, становится легче, если вы проигнорируете тот факт, что в каждой точке есть ровно один интервал с центром, и просто рассматривайте его как набор начальных и конечных точек интервалов. Затем вы можете просто сканировать его слева (код 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

Ответ 5

Здесь O (N) время, O (N) пространственный алгоритм, требующий 3 пробега по массиву и без сортировки, проверенный результат 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;
}

Ответ 6

Python 100/100 (тестируется) на кодовости, с O (nlogn) временем и O (n) пространством.

Ниже приведена реализация @noisyboiler python метода @Aryabhatta с комментариями и примером. Полный кредит оригинальным авторам, любые ошибки/плохая формулировка - это полностью моя ошибка.

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),    

Ответ 7

Я получил 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;
}

Ответ 8

Ответ на 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

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

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) настолько медленный, но он работает.

Ответ 11

Это рубиновое решение, набравшее 100/100 на кодовость. Я отправляю его сейчас, потому что мне трудно следовать уже опубликованному рубиновому ответу.

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

Ответ 12

100/100 С#

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

Ответ 13

Вероятно, очень быстро. НА). Но вы должны проверить это. 100% на Codility. Основная идея: 1. В любой точке стола есть количество кружков, "открытых" до правого края круга, скажем "о". 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

Здесь уже есть много хороших ответов, в том числе отличное объяснение из принятого ответа. Тем не менее, я хотел бы отметить небольшое замечание о деталях реализации на языке 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 - я - 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 позволяет вам делать это очень легко :) Возможно, это не очень хорошая идея, но для меня это был хороший урок, чтобы уделять больше внимания этим "техническим" моментам, когда преобразование идей и алгоритмов в реальные решения.

Ответ 15

Решение Swift 4 100% (Codility не проверяют наихудший случай для этого решения)

public func solution(_ A : inout [Int]) -> Int {
    // write your code in Swift 4.2.1 (Linux)
    var count = 0
    let sortedA = A.sorted(by: >)
    if sortedA.isEmpty{ return 0 }
    let maxVal = sortedA[0]

    for i in 0..<A.count{
        let maxIndex = min(i + A[i] + maxVal + 1,A.count)
        for j in i + 1..<maxIndex{
            if j - A[j] <= i + A[i]{
                count += 1
            }
        }

        if count > 10_000_000{
            return -1
        }
    }

    return count
}

Ответ 16

Это получило 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));

    }
}

}

Ответ 17

Спасибо Фальку за отличную идею! Вот рубиновая реализация, которая использует разреженность.

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]

Ответ 18

Вот код PHP, который набрал 100 на кодовости:

$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;

Однако я не понимаю логику. Это просто преобразованный код С++ сверху. Люди, не могли бы вы рассказать о том, что вы здесь делаете, пожалуйста?

Ответ 19

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

Ответ 20

Реализация идеи, изложенная выше в 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);
}
}

Ответ 21

93% оценка http://codility.com/demo/results/demoUWFUDS-6XY/ Только один тест не работает почему?

// you can also use includes, for example:
#include <algorithm>
#include <map>


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

int solution ( 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
    long 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;
}

Я попытался установить также предел в случае, если размеp > 100000, и я потерял точку в этом случае, поэтому он не понял, какой тест они делают, чтобы терпеть неудачу.

Ответ 22

Реализация 100/100 С#, как описано Aryabhatta (решение для двоичного поиска).

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

Ответ 23

100% баллов в Codility.

Ниже приведена адаптация к С# Толя :

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

Ответ 24

Здесь двухпроходное решение на С++, которое не требует каких-либо библиотек, двоичного поиска, сортировки и т.д.

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

Ответ 25

Мой ответ в Swift; получает 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
}

Ответ 26

Решение С# 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;
    }
}

Ответ 27

Рубиновый раствор. Оценка 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

Ответ 28

С# 100/100 с временной сложностью O(N*log(N)) и сложностью пространства O(N).

Основные идеи:

  • Сделайте 2 отсортированных массива: левые и правые.
  • Объединить эти массивы в один логический массив, где true означает "открытие", а false означает "закрытие" интервала.
  • Запустите булевский массив и подсчитайте открытые интервалы, суммируйте их.

_

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

Ответ 29

Ниже приведена реализация принятого ответа @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% баллом.

Ответ 30

Вот мое чистое решение на Python (без импорта библиотек). Эта логика может быть адаптирована к любому другому языку программирования.

Я получил 100% в Codility - Время: 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