Étant donné un tableau A
d'entiers N
, nous traçons des disques N
dans un plan 2D, de sorte que le i-ème disque ait un centre dans (0,i)
et un rayon A[i]
. On dit que le k-ème disque et le j-ème disque se croisent, si les k-ème et j-ème disques ont au moins un point commun.
Écrire une fonction
int number_of_disc_intersections(int[] A);
qui donne un tableau A
décrivant N
disques comme expliqué ci-dessus, retourne le nombre de paires de disques se croisant. Par exemple, étant donné N=6
et
A[0] = 1
A[1] = 5
A[2] = 2
A[3] = 1
A[4] = 4
A[5] = 0
il y a 11 paires de disques se croisant:
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
la fonction doit donc renvoyer 11. La fonction doit renvoyer -1 si le nombre de paires qui se croisent dépasse 10 000 000. La fonction peut supposer que N
ne dépasse pas 10 000 000.
Donc, vous voulez trouver le nombre d'intersections des intervalles [i-A[i], i+A[i]]
.
Conservez un tableau trié (appelez-le X) contenant le i-A[i]
(prévoyez également un espace supplémentaire contenant la valeur i+A[i]
).
Parcourez maintenant le tableau X en commençant à l’intervalle le plus à gauche (le plus petit i-A[i]
).
Pour l’intervalle en cours, effectuez une recherche binaire pour voir où le point final de l’intervalle (ie i+A[i]
) va (appelé le rang). Maintenant, vous savez qu'il coupe tous les éléments à gauche.
Incrémenter un compteur avec le rang et soustraire la position actuelle (en supposant un indexé) car nous ne voulons pas compter les intervalles et les auto-intersections en double.
O(nlogn) heure, O(n) espace.
Complexité O (N) et solution de mémoire 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;
}
Eh bien, j'ai adapté l'idée de Falk Hüffner au c ++ et apporté un changement dans la plage . Contrairement à ce qui est écrit ci-dessus, il n'est pas nécessaire de sortir du champ de la matrice ) . Sur Codility ce code a reçu 100% . Merci Falk pour votre bonne idée!
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;
}
Cela peut même être fait en temps linéaire. En fait, cela devient plus facile si vous ignorez le fait qu’il existe exactement un intervalle centré sur chaque point et que vous le traitez simplement comme un ensemble de points de départ et d’intervalle. Vous pouvez ensuite simplement le scanner à partir de la gauche (code Python par souci de simplicité):
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
Voici un temps O(N), l'algorithme d'espace O(N) nécessitant 3 passages dans le tableau et aucun tri, score vérifié 100% :
Vous êtes intéressé par des paires de disques. Chaque paire implique un côté d’un disque et l’autre côté de l’autre disque. Par conséquent, nous n'aurons pas de paires dupliquées si nous traitons un côté de chaque disque. Appelons les côtés à droite et à gauche (j'ai fait pivoter l'espace en y réfléchissant).
Un chevauchement est dû soit à un côté droit recouvrant un autre disque directement au centre (donc des paires égales au rayon avec une certaine précaution quant à la longueur de la matrice), soit au nombre de côtés gauche existant au niveau du bord le plus à droite.
Nous créons donc un tableau contenant le nombre de côtés gauche en chaque point, puis une simple somme.
Code 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;
}
Python 100/100 (testé) sur la codilité, avec le temps O(nlogn) et le O(n).
Voici l'implémentation en python de la méthode de @ Aryabhatta par @ noisyboiler avec des commentaires et un exemple .
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
Exemple travaillé: étant donné [3, 0, 1, 6], les rayons du disque ressemblent à ceci:
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),
J'ai eu 100 sur 100 avec cette implémentation C++:
#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;
}
Java 2 * 100%.
result
est déclaré aussi long qu'un cas, la codilité n'est pas testée, à savoir 50k * 50k intersections en un point.
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;
}
}
Une réponse en 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
Probablement extrêmement rapide. SUR). Mais vous devez vérifier. 100% sur la codilité . Idée principale: 1. En tout point du tableau, il y a un nombre de cercles "ouvert" jusqu'au bord droit du cercle, disons "o" . 2. Donc, il y a (o-1-utilisé) paires possibles pour le cercle en ce point. "utilisé" signifie que le cercle a été traité et que les paires sont comptées.
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;
}
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;
}
}
Ceci est une solution Ruby qui a marqué 100/100 sur la codilité. Je la poste maintenant parce que je trouve difficile de suivre la réponse de Ruby déjà publiée.
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
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++;
}
}
C'est O(N^2)
si lent, mais ça marche.
C # 100/100 avec O(N*log(N))
complexité temporelle et O(N)
complexité spatiale.
Les idées principales:
true
signifie "ouverture" et false
signifie "fermeture" d'un intervalle._
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;
}
Score de 100% dans Codility .
Voici une adaptation au C # de Толяsolution :
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;
}
Cela a 100/100 en c #
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));
}
}
}
Une implémentation de l'idée énoncée ci-dessus en 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);
}
}
#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;
}
Solution 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;
}
}
93% de points http://codility.com/demo/results/demoUWFUDS-6XY/ Un seul test ne fonctionne pas pourquoi?
// 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;
}
J'ai essayé de mettre aussi une limite au cas où la taille est> 100000 et j'ai perdu le point dans ce cas donc on ne sait pas quel test ils font pour échouer.
Voici le code PHP ayant obtenu 100 sur la codilité:
$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;
Cependant, je ne comprends pas la logique. Ceci est juste transformé le code C++ d'en haut. Les gens, pouvez-vous préciser ce que vous faisiez ici, s'il vous plaît?
Merci à Falk pour la bonne idée! Voici une implémentation de Ruby qui tire parti de la rareté.
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]
Il y a déjà beaucoup de bonnes réponses ici, y compris la bonne explication de la réponse acceptée. Cependant, je voulais signaler une petite observation concernant les détails de la mise en œuvre dans le langage Python.
À l'origine, j'ai proposé la solution ci-dessous. Je m'attendais à avoir la complexité temporelle de O(N*log(N))
dès que nous aurons une seule boucle for avec des itérations N
et que chaque itération effectue une recherche binaire qui prend au plus 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
Cependant, j'ai O(N**2)
et un échec de dépassement de délai. Voyez-vous ce qui ne va pas ici? Oui, cette ligne:
pos = bisect.bisect_right(lefts[i+1:], r)
Dans cette ligne, vous êtes en fait en prenant une copie de la liste d'origine pour la passer à la fonction de recherche binaire, ce qui ruine totalement l'efficacité de la solution proposée! Cela rend votre code un peu plus consice (c’est-à-dire que vous n’avez pas besoin d’écrire pos - i - 1
), mais nuit considérablement aux performances. Ainsi, comme il a été montré ci-dessus, la solution devrait être:
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
Il semble que parfois on puisse être trop pressé de faire des coupes et des copies parce que Python vous permet de le faire aussi facilement :) Ce n’est probablement pas un bon aperçu, mais pour moi, c’était une bonne leçon de porter plus d’attention à ces moments convertir des idées et des algorithmes en solutions Word réelles.
Voici ma solution propre en Python (sans importer des bibliothèques). Cette logique peut être adaptée à n’importe quel autre langage de programmation.
J'ai 100% en codilité - Temps: O(Nlog(N)) - Espace: O (N)
J'espère que ça aide.
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
Voici une solution C++ en deux étapes qui ne nécessite aucune bibliothèque, recherche binaire, tri, etc.
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;
}
Une solution Ruby. Scores 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
Une implémentation 100/100 C # comme décrit par Aryabhatta (la solution de recherche binaire).
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;
}
}
Ci-dessous, une implémentation de la réponse acceptée par @Aryabhatta à Kotlin, donc tout le mérite revient à @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
}
Solution de codilité avec un score de 100%.
Ma réponse à Swift; obtient un score de 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
}
Swift 4 Solution 100% (Codility ne vérifie pas le pire des cas pour cette solution)
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
}