Related
I am trying to solve this problem. I believe my solution does work, but it takes more time. This is my solution - at each step, I calculate the minimum sum if I choose i or i+1 index.
class Solution
{
public int minimumTotal(List<List<Integer>> triangle)
{
return minSum( triangle, 0, 0 );
}
public int minSum( List<List<Integer>> triangle, int row, int index )
{
if( row >= triangle.size() )
return 0;
int valueAtThisRow = triangle.get(row).get(index);
return Math.min( valueAtThisRow + minSum(triangle, row+1, index),
valueAtThisRow + minSum(triangle, row+1, index+1));
}
}
I think more appropriate way is to use DP. Please share any suggestions on how I can convert this to a DP.
I think that bottom-up solution is simpler here and does not require additional memory, we can store intermediate results in the same list/array cells
Walk through levels of triangle starting from the level before last, choosing the best result from two possible for every element, then move to upper level and so on. After that triangle[0][0] will contain minimum sum
for (row = n - 2; row >= 0; row--)
for (i = 0; i <= row; i++)
triangle[row][i] += min(triangle[row+1][i], triangle[row+1][i+1])
(tried python version, accepted)
DP bottom up approach:
class Solution {
public:
int minimumTotal(vector<vector<int> > &triangle)
{
vector<int> mini = triangle[triangle.size()-1];
for ( int i = triangle.size() - 2; i>= 0 ; --i )
for ( int j = 0; j < triangle[i].size() ; ++ j )
mini[j] = triangle[i][j] + min(mini[j],mini[j+1]);
return mini[0];
}
};
Python version:
def minimumTotal(self, triangle: List[List[int]]) -> int:
if not triangle: return
size = len(triangle)
res = triangle[-1] # last row
for r in range(size-2, -1, -1): # bottom up
for c in range(len(triangle[r])):
res[c] = min(res[c], res[c+1]) + triangle[r][c]
return res[0]
Top down solution: we compute shortest paths (agenda) to each cells row by row starting from the triangle's top.
C# code
public int MinimumTotal(IList<IList<int>> triangle) {
int[] agenda = new int[] {triangle[0][0]};
for (int r = 1; r < triangle.Count; ++r) {
int[] next = triangle[r].ToArray();
for (int c = 0; c < next.Length; ++c)
if (c == 0)
next[c] += agenda[c];
else if (c == next.Length - 1)
next[c] += agenda[c - 1];
else
next[c] += Math.Min(agenda[c - 1], agenda[c]);
agenda = next;
}
return agenda.Min();
}
I have a path in 2D space. But distances between noded are not equal.
I'm looking for an algorithm that adds nodes to the source path so that the distance between nodes will be equal.
What is the best practice?
Example picture:
It is geometrically impossible for you to generate equidistant points for more than one arbitrary path segment - only possible if their lengths share a common divisor.
However, you can generate the closest matching set of points using the following method:
You need to first set the maximum number of points N you want on a path segment. This is to stop the algorithm from infinitely looping - because in the general case only an infinite number of divisions would give us the exact answer, and that is not what we want.
However before this can be applied, we need to check that N + 1 is not less than the ratio of the longest path to the shortest. If it is then we need to adjust it.
For each path segment iterate to the maximum number of points N, calculating the division length L for each number. For each iterated value we shall define a Cost variable as the sum of the total difference between the computed solution and the ideal.
Then iterate through every other path segment. Divide its length M by L to give a ratio R:
If R is an integer, then for this segment an exact solution has been found. Add zero to Cost
Otherwise take A = floor(R), B = ceil(R). Compute the two separate costs cost_A = abs(M - L * A) and similarly for B.
If cost_A < cost_B, take C = A as the optimal division count for this segment, and vice versa. Record C.
Take min(cost_A, cost_B) and add to Cost. Continue.
Remember to keep track of a list of optimal values for C for each path segment, and also the "working list" which records the current computation. Also keep track of a min_Cost variable.
If at the end of a main loop for each segment Cost < min_Cost, then update min_Cost and the optimal list.
The above description may seem a little vague. Here is some C# code - apologies as I'm not familiar with the details of C# Mono / Unity, so you may have to replace a few type names / function names here or there, but the gist of the algorithm is hopefully what you want.
public static int[] calculateOptimalSplitNumbers(Point[] path, int N)
{
int no_segs = path.Length - 1;
if (no_segs <= 1) return null;
double[] lengths = new double[no_segs];
for (int i = 0; i < no_segs; i++)
lengths[i] = Vector.LengthOf(path[i + 1] - path[i]); // replace with the correct unity function?
int max_ratio = Math.Floor(Math.Max(lengths) / Math.Min(lengths)) - 1;
if (N < max_ratio)
N = max_ratio;
double min_Cost = double.MaxValue;
int[] min_List = new int[no_segs];
int[] cur_List = new int[no_segs];
for (int i = 0; i < no_segs; i++)
{
double cost = 0.0;
for (int j = 0; j < N; j++)
{
double L = lengths[i] / (j + 2);
cur_list[i] = j + 1;
for (int k = 0; k < no_segs; k++)
{
if (k == i) continue;
double M = lengths[k],
R = M / L;
// path is too short - put no points
if (R < 1.0) {
cur_list[k] = 0;
cost += M - L;
}
int A = Math.Floor(R),
B = Math.Ceiling(R);
double cost_A = Math.Abs(M - L * A),
cost_B = Math.Abs(M - L * B);
if (cost_A < cost_B) {
cur_list[k] = A;
cost += cost_A;
}
else {
cur_list[k] = B;
cost += cost_B;
}
}
}
if (cost < min_Cost) {
min_Cost = cost;
System.Array.Copy(cur_List, min_List, no_segs);
}
}
return min_List;
}
The code takes an array of path points and returns the number of points to put on each path segment. If you need any more explanation of the code just let me know and I will edit with some more comments.
I have decided to share path normalizer utility (according to #meowgoesthedog approach) for using it in Unity3D:
using System; using System.Collections.Generic; using UnityEngine;
/// <summary>
/// Represents helper that normalizes the path in a way
/// that distance between all nodes become almost equal.
/// </summary>
public static class PathNormalizer
{
/// <summary>
/// Normalizes the specified vector path.
/// </summary>
/// <param name="vectorPath">The vector path.</param>
/// <param name="minSplitsBySegment">The minimum splits by segment.</param>
public static Vector3[] Normalize(Vector3[] vectorPath, int minSplitsBySegment)
{
if (vectorPath.Length < 3)
{
return vectorPath;
}
var segmentsSplits = CalculateOptimalSplitNumbers(vectorPath, minSplitsBySegment);
if (segmentsSplits == null)
{
Debug.LogWarning("Can't normalize path");
return vectorPath;
}
List<Vector3> newPath = new List<Vector3>();
for (int i = 1; i < vectorPath.Length; i++)
{
var split = segmentsSplits[i - 1];
for (int j = 0; j < split; j++)
{
var newNode = Vector3.Lerp(vectorPath[i - 1], vectorPath[i], (float)j / split);
newPath.Add(newNode);
}
}
newPath.Add(vectorPath[vectorPath.Length - 1]);
return newPath.ToArray();
}
private static int[] CalculateOptimalSplitNumbers(Vector3[] path, int minSplitsBySegment)
{
int noSegs = path.Length - 1;
if (noSegs <= 1) return null;
float[] lengths = new float[noSegs];
for (int i = 0; i < noSegs; i++)
lengths[i] = Vector3.Distance(path[i + 1], path[i]);
float minLenght = float.MaxValue;
float maxLenght = 0;
foreach (var length in lengths)
{
if (length < minLenght)
{
minLenght = length;
}
if (length > maxLenght)
{
maxLenght = length;
}
}
int maxRatio = (int)Math.Floor(maxLenght / minLenght) - 1;
if (minSplitsBySegment < maxRatio)
minSplitsBySegment = maxRatio;
double minCost = double.MaxValue;
int[] minList = new int[noSegs];
int[] curList = new int[noSegs];
for (int i = 0; i < noSegs; i++)
{
double cost = 0.0;
for (int j = 0; j < minSplitsBySegment; j++)
{
double l = lengths[i] / (j + 2);
curList[i] = j + 1;
for (int k = 0; k < noSegs; k++)
{
if (k == i) continue;
double m = lengths[k],
r = m / l;
// path is too short - put no points
if (r < 1.0)
{
curList[k] = 0;
cost += m - l;
}
int a = (int)Math.Floor(r),
b = (int)Math.Ceiling(r);
double costA = Math.Abs(m - l * a),
costB = Math.Abs(m - l * b);
if (costA < costB)
{
curList[k] = a;
cost += costA;
}
else
{
curList[k] = b;
cost += costB;
}
}
}
if (cost < minCost)
{
minCost = cost;
Array.Copy(curList, minList, noSegs);
}
}
return minList;
}
}
I recently came across an interview question asked by Amazon and I am not able to find an optimized algorithm to solve this question:
You are given an input array whose each element represents the height of a line towers. The width of every tower is 1. It starts raining. How much water is collected between the towers?
Example
Input: [1,5,3,7,2] , Output: 2 units
Explanation: 2 units of water collected between towers of height 5 and 7
*
*
*w*
*w*
***
****
*****
Another Example
Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units
Explanation= 2 units of water collected between towers of height 5 and 7 +
4 units of water collected between towers of height 7 and 6 +
1 units of water collected between towers of height 6 and 5 +
2 units of water collected between towers of height 6 and 9 +
4 units of water collected between towers of height 7 and 9 +
1 units of water collected between towers of height 9 and 2.
At first I thought this could be solved by Stock-Span Problem (http://www.geeksforgeeks.org/the-stock-span-problem/) but I was wrong so it would be great if anyone can think of a time-optimized algorithm for this question.
Once the water's done falling, each position will fill to a level equal to the smaller of the highest tower to the left and the highest tower to the right.
Find, by a rightward scan, the highest tower to the left of each position. Then find, by a leftward scan, the highest tower to the right of each position. Then take the minimum at each position and add them all up.
Something like this ought to work:
int tow[N]; // nonnegative tower heights
int hl[N] = {0}, hr[N] = {0}; // highest-left and highest-right
for (int i = 0; i < n; i++) hl[i] = max(tow[i], (i!=0)?hl[i-1]:0);
for (int i = n-1; i >= 0; i--) hr[i] = max(tow[i],i<(n-1) ? hr[i+1]:0);
int ans = 0;
for (int i = 0; i < n; i++) ans += min(hl[i], hr[i]) - tow[i];
Here's an efficient solution in Haskell
rainfall :: [Int] -> Int
rainfall xs = sum (zipWith (-) mins xs)
where mins = zipWith min maxl maxr
maxl = scanl1 max xs
maxr = scanr1 max xs
it uses the same two-pass scan algorithm mentioned in the other answers.
Refer this website for code, its really plain and simple
http://learningarsenal.info/index.php/2015/08/21/amount-of-rain-water-collected-between-towers/
Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units
Explanation
Each tower can hold water upto a level of smallest height between heighest tower to left, and highest tower to the right.
Thus we need to calculate highest tower to left on each and every tower, and likewise for the right side.
Here we will be needing two extra arrays for holding height of highest tower to left on any tower say, int leftMax[] and likewise for right side say int rightMax[].
STEP-1
We make a left pass of the given array(i.e int tower[]),and will be maintaining a temporary maximum(say int tempMax) such that on each iteration height of each tower will be compared to tempMax, and if height of current tower is less than tempMax then tempMax will be set as highest tower to left of it, otherwise height of current tower will be assigned as the heighest tower to left and tempMax will be updated with current tower height,
STEP-2
We will be following above procedure only as discussed in STEP-1 to calculate highest tower to right BUT this times making a pass through array from right side.
STEP-3
The amount of water which each tower can hold is-
(minimum height between highest right tower and highest left tower) – (height of tower)
You can do this by scanning the array twice.
The first time you scan from top to bottom and store the value of the tallest tower you have yet to encounter when reaching each row.
You then repeat the process, but in reverse. You start from the bottom and work towards the top of the array. You keep track of the tallest tower you have seen so far and compare the height of it to the value for that tower in the other result set.
Take the difference between the lesser of these two values (the shortest of the tallest two towers surrounding the current tower, subtract the height of the tower and add that amount to the total amount of water.
int maxValue = 0;
int total = 0;
int[n] lookAhead
for(i=0;i<n;i++)
{
if(input[i] > maxValue) maxValue = input[i];
lookahead[i] = maxValue;
}
maxValue = 0;
for(i=n-1;i>=0;i--)
{
// If the input is greater than or equal to the max, all water escapes.
if(input[i] >= maxValue)
{
maxValue = input[i];
}
else
{
if(maxValue > lookAhead[i])
{
// Make sure we don't run off the other side.
if(lookAhead[i] > input[i])
{
total += lookAhead[i] - input[i];
}
}
else
{
total += maxValue - input[i];
}
}
}
Readable Python Solution:
def water_collected(heights):
water_collected = 0
left_height = []
right_height = []
temp_max = heights[0]
for height in heights:
if (height > temp_max):
temp_max = height
left_height.append(temp_max)
temp_max = heights[-1]
for height in reversed(heights):
if (height > temp_max):
temp_max = height
right_height.insert(0, temp_max)
for i, height in enumerate(heights):
water_collected += min(left_height[i], right_height[i]) - height
return water_collected
O(n) solution in Java, single pass
Another implementation in Java, finding the water collected in a single pass through the list. I scanned the other answers but didn't see any that were obviously using my solution.
Find the first "peak" by looping through the list until the tower height stops increasing. All water before this will not be collected (drain off to the left).
For all subsequent towers:
If the height of the subsequent tower decreases or stays the same, add water to a "potential collection" bucket, equal to the difference between the tower height and the previous max tower height.
If the height of the subsequent tower increases, we collect water from the previous bucket (subtract from the "potential collection" bucket and add to the collected bucket) and also add water to the potential bucket equal to the difference between the tower height and the previous max tower height.
If we find a new max tower, then all the "potential water" is moved into the collected bucket and this becomes the new max tower height.
In the example above, with input: [5,3,7,2,6,4,5,9,1,2], the solution works as follows:
5: Finds 5 as the first peak
3: Adds 2 to the potential bucket (5-3) collected = 0, potential = 2
7: New max, moves all potential water to the collected bucket collected = 2, potential = 0
2: Adds 5 to the potential bucket (7-2) collected = 2, potential = 5
6: Moves 4 to the collected bucket and adds 1 to the potential bucket (6-2, 7-6) collected = 6, potential = 2
4: Adds 2 to the potential bucket (6-4) collected = 6, potential = 4
5: Moves 1 to the collected bucket and adds 2 to the potential bucket (5-4, 7-5) collected = 7, potential = 6
9: New max, moves all potential water to the collected bucket collected = 13, potential = 0
1: Adds 8 to the potential bucket (9-1) collected = 13, potential = 8
2: Moves 1 to the collected bucket and adds 7 to the potential bucket (2-1, 9-2) collected = 14, potential = 15
After running through the list once, collected water has been measured.
public static int answer(int[] list) {
int maxHeight = 0;
int previousHeight = 0;
int previousHeightIndex = 0;
int coll = 0;
int temp = 0;
// find the first peak (all water before will not be collected)
while(list[previousHeightIndex] > maxHeight) {
maxHeight = list[previousHeightIndex];
previousHeightIndex++;
if(previousHeightIndex==list.length) // in case of stairs (no water collected)
return coll;
else
previousHeight = list[previousHeightIndex];
}
for(int i = previousHeightIndex; i<list.length; i++) {
if(list[i] >= maxHeight) { // collect all temp water
coll += temp;
temp = 0;
maxHeight = list[i]; // new max height
}
else {
temp += maxHeight - list[i];
if(list[i] > previousHeight) { // we went up... collect some water
int collWater = (i-previousHeightIndex)*(list[i]-previousHeight);
coll += collWater;
temp -= collWater;
}
}
// previousHeight only changes if consecutive towers are not same height
if(list[i] != previousHeight) {
previousHeight = list[i];
previousHeightIndex = i;
}
}
return coll;
}
None of the 17 answers already posted are really time-optimal.
For a single processor, a 2 sweep (left->right, followed by a right->left summation) is optimal, as many people have pointed out, but using many processors, it is possible to complete this task in O(log n) time. There are many ways to do this, so I'll explain one that is fairly close to the sequential algorithm.
Max-cached tree O(log n)
1: Create a binary tree of all towers such that each node contains the height of the highest tower in any of its children. Since the two leaves of any node can be computed independently, this can be done in O(log n) time with n cpu's. (Each value is handled by its own cpu, and they build the tree by repeatedly merging two existing values. All parallel branches can be executed in parallel. Thus, it's O(log2(n)) for a 2-way merge function (max, in this case)).
2a: Then, for each node in the tree, starting at the root, let the right leaf have the value max(left, self, right). This will create the left-to-right monotonic sweep in O(log n) time, using n cpu's.
2b: To compute the right-to-left sweep, we do the same procedure as before. Starting with root of the max-cached tree, let the left leaf have the value max(left, self, right). These left-to-right (2a) and right-to-left (2b) sweeps can be done in parallel if you'd like to. They both use the max-cached tree as input, and generate one new tree each (or sets their own fields in original tree, if you prefer that).
3: Then, for each tower, the amount of water on it is min(ltr, rtl) - towerHeight, where ltr is the value for that tower in the left-to-right monotonic sweep we did before, i.e. the maximum height of any tower to the left of us (including ourselves1), and rtl is the same for the right-to-left sweep.
4: Simply sum this up using a tree in O(log n) time using n cpu's, and we're done.
1 If the current tower is taller than all towers to the left of us, or taller than all towers to the the right of us, min(ltr, rtl) - towerHeight is zero.
Here's two other ways to do it.
Here is a solution in Groovy in two passes.
assert waterCollected([1, 5, 3, 7, 2]) == 2
assert waterCollected([5, 3, 7, 2, 6, 4, 5, 9, 1, 2]) == 14
assert waterCollected([5, 5, 5, 5]) == 0
assert waterCollected([5, 6, 7, 8]) == 0
assert waterCollected([8, 7, 7, 6]) == 0
assert waterCollected([6, 7, 10, 7, 6]) == 0
def waterCollected(towers) {
int size = towers.size()
if (size < 3) return 0
int left = towers[0]
int right = towers[towers.size() - 1]
def highestToTheLeft = []
def highestToTheRight = [null] * size
for (int i = 1; i < size; i++) {
// Track highest tower to the left
if (towers[i] < left) {
highestToTheLeft[i] = left
} else {
left = towers[i]
}
// Track highest tower to the right
if (towers[size - 1 - i] < right) {
highestToTheRight[size - 1 - i] = right
} else {
right = towers[size - 1 - i]
}
}
int water = 0
for (int i = 0; i < size; i++) {
if (highestToTheLeft[i] && highestToTheRight[i]) {
int minHighest = highestToTheLeft[i] < highestToTheRight[i] ? highestToTheLeft[i] : highestToTheRight[i]
water += minHighest - towers[i]
}
}
return water
}
Here same snippet with an online compiler:
https://groovy-playground.appspot.com/#?load=3b1d964bfd66dc623c89
You can traverse first from left to right, and calculate the water accumulated for the cases where there is a smaller building on the left and a larger one on the right. You would have to subtract the area of the buildings that are in between these two buildings and are smaller than the left one.
Similar would be the case for right to left.
Here is the code for left to right. I have uploaded this problem on leetcode online judge using this approach.
I find this approach much more intuitive than the standard solution which is present everywhere (calculating the largest building on the right and the left for each i ).
int sum=0, finalAns=0;
idx=0;
while(a[idx]==0 && idx < n)
idx++;
for(int i=idx+1;i<n;i++){
while(a[i] < a[idx] && i<n){
sum += a[i];
i++;
}
if(i==n)
break;
jdx=i;
int area = a[idx] * (jdx-idx-1);
area -= sum;
finalAns += area;
idx=jdx;
sum=0;
}
The time complexity of this approach is O(n), as you are traversing the array two time linearly.
Space complexity would be O(1).
The first and the last bars in the list cannot trap water. For the remaining towers, they can trap water when there are max heights to the left and to the right.
water accumulation is:
max( min(max_left, max_right) - current_height, 0 )
Iterating from the left, if we know that there is a max_right that is greater, min(max_left, max_right) will become just max_left. Therefore water accumulation is simplified as:
max(max_left - current_height, 0) Same pattern when considering from the right side.
From the info above, we can write a O(N) time and O(1) space algorithm as followings(in Python):
def trap_water(A):
water = 0
left, right = 1, len(A)-1
max_left, max_right = A[0], A[len(A)-1]
while left <= right:
if A[left] <= A[right]:
max_left = max(A[left], max_left)
water += max(max_left - A[left], 0)
left += 1
else:
max_right = max(A[right], max_right)
water += max(max_right - A[right], 0)
right -= 1
return water
/**
* #param {number[]} height
* #return {number}
*/
var trap = function(height) {
let maxLeftArray = [], maxRightArray = [];
let maxLeft = 0, maxRight = 0;
const ln = height.length;
let trappedWater = 0;
for(let i = 0;i < height.length; i ++) {
maxLeftArray[i] = Math.max(height[i], maxLeft);
maxLeft = maxLeftArray[i];
maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
maxRight = maxRightArray[ln - i - 1];
}
for(let i = 0;i < height.length; i ++) {
trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
}
return trappedWater;
};
var arr = [5,3,7,2,6,4,5,9,1,2];
console.log(trap(arr));
You could read the detailed explanation in my blogpost: trapping-rain-water
Here is one more solution written on Scala
def find(a: Array[Int]): Int = {
var count, left, right = 0
while (left < a.length - 1) {
right = a.length - 1
for (j <- a.length - 1 until left by -1) {
if (a(j) > a(right)) right = j
}
if (right - left > 1) {
for (k <- left + 1 until right) count += math.min(a(left), a(right)) - a(k)
left = right
} else left += 1
}
count
}
An alternative algorithm in the style of Euclid, which I consider more elegant than all this scanning is:
Set the two tallest towers as the left and right tower. The amount of
water contained between these towers is obvious.
Take the next tallest tower and add it. It must be either between the
end towers, or not. If it is between the end towers it displaces an
amount of water equal to the towers volume (thanks to Archimedes for
this hint). If it outside the end towers it becomes a new end tower
and the amount of additional water contained is obvious.
Repeat for the next tallest tower until all towers are added.
I've posted code to achieve this (in a modern Euclidean idiom) here: http://www.rosettacode.org/wiki/Water_collected_between_towers#F.23
I have a solution that only requires a single traversal from left to right.
def standing_water(heights):
if len(heights) < 3:
return 0
i = 0 # index used to iterate from left to right
w = 0 # accumulator for the total amount of water
while i < len(heights) - 1:
target = i + 1
for j in range(i + 1, len(heights)):
if heights[j] >= heights[i]:
target = j
break
if heights[j] > heights[target]:
target = j
if target == i:
return w
surface = min(heights[i], heights[target])
i += 1
while i < target:
w += surface - heights[i]
i += 1
return w
An intuitive solution for this problem is one in which you bound the problem and fill water based on the height of the left and right bounds.
My solution:
Begin at the left, setting both bounds to be the 0th index.
Check and see if there is some kind of a trajectory (If you were to
walk on top of these towers, would you ever go down and then back up
again?) If that is the case, then you have found a right bound.
Now back track and fill the water accordingly (I simply added the
water to the array values themselves as it makes the code a little
cleaner, but this is obviously not required).
The punch line: If the left bounding tower height is greater than the
right bounding tower height than you need to increment the right
bound. The reason is because you might run into a higher tower and need to fill some more water.
However, if the right tower is higher than the left tower then no
more water can be added in your current sub-problem. Thus, you move
your left bound to the right bound and continue.
Here is an implementation in C#:
int[] towers = {1,5,3,7,2};
int currentMinimum = towers[0];
bool rightBoundFound = false;
int i = 0;
int leftBoundIndex = 0;
int rightBoundIndex = 0;
int waterAdded = 0;
while(i < towers.Length - 1)
{
currentMinimum = towers[i];
if(towers[i] < currentMinimum)
{
currentMinimum = towers[i];
}
if(towers[i + 1] > towers[i])
{
rightBoundFound = true;
rightBoundIndex = i + 1;
}
if (rightBoundFound)
{
for(int j = leftBoundIndex + 1; j < rightBoundIndex; j++)
{
int difference = 0;
if(towers[leftBoundIndex] < towers[rightBoundIndex])
{
difference = towers[leftBoundIndex] - towers[j];
}
else if(towers[leftBoundIndex] > towers[rightBoundIndex])
{
difference = towers[rightBoundIndex] - towers[j];
}
else
{
difference = towers[rightBoundIndex] - towers[j];
}
towers[j] += difference;
waterAdded += difference;
}
if (towers[leftBoundIndex] > towers[rightBoundIndex])
{
i = leftBoundIndex - 1;
}
else if (towers[rightBoundIndex] > towers[leftBoundIndex])
{
leftBoundIndex = rightBoundIndex;
i = rightBoundIndex - 1;
}
else
{
leftBoundIndex = rightBoundIndex;
i = rightBoundIndex - 1;
}
rightBoundFound = false;
}
i++;
}
I have no doubt that there are more optimal solutions. I am currently working on a single-pass optimization. There is also a very neat stack implementation of this problem, and it uses a similar idea of bounding.
Here is my solution, it passes this level and pretty fast, easy to understand
The idea is very simple: first, you figure out the maximum of the heights (it could be multiple maximum), then you chop the landscape into 3 parts, from the beginning to the left most maximum heights, between the left most max to the right most max, and from the right most max to the end.
In the middle part, it's easy to collect the rains, one for loop does that. Then for the first part, you keep on updating the current max height that is less than the max height of the landscape. one loop does that. Then for the third part, you reverse what you have done to the first part
def answer(heights):
sumL = 0
sumM = 0
sumR = 0
L = len(heights)
MV = max(heights)
FI = heights.index(MV)
LI = L - heights[::-1].index(MV) - 1
if LI-FI>1:
for i in range(FI+1,LI):
sumM = sumM + MV-heights[i]
if FI>0:
TM = heights[0]
for i in range(1,FI):
if heights[i]<= TM:
sumL = sumL + TM-heights[i]
else:
TM = heights[i]
if LI<(L-1):
TM = heights[-1]
for i in range(L-1,LI,-1):
if heights[i]<= TM:
sumL = sumL + TM-heights[i]
else:
TM = heights[i]
return(sumL+sumM+sumR)
Here is a solution in JAVA that traverses the list of numbers once. So the worst case time is O(n). (At least that's how I understand it).
For a given reference number keep looking for a number which is greater or equal to the reference number. Keep a count of numbers that was traversed in doing so and store all those numbers in a list.
The idea is this. If there are 5 numbers between 6 and 9, and all the five numbers are 0's, it means that a total of 30 units of water can be stored between 6 and 9. For a real situation where the numbers in between aren't 0's, we just deduct the total sum of the numbers in between from the total amount if those numbers were 0. (In this case, we deduct from 30). And that will give the count of water stored in between these two towers. We then save this amount in a variable called totalWaterRetained and then start from the next tower after 9 and keep doing the same till the last element.
Adding all the instances of totalWaterRetained will give us the final answer.
JAVA Solution: (Tested on a few inputs. Might be not 100% correct)
private static int solveLineTowerProblem(int[] inputArray) {
int totalWaterContained = 0;
int index;
int currentIndex = 0;
int countInBetween = 0;
List<Integer> integerList = new ArrayList<Integer>();
if (inputArray.length < 3) {
return totalWaterContained;
} else {
for (index = 1; index < inputArray.length - 1;) {
countInBetween = 0;
integerList.clear();
int tempIndex = index;
boolean flag = false;
while (inputArray[currentIndex] > inputArray[tempIndex] && tempIndex < inputArray.length - 1) {
integerList.add(inputArray[tempIndex]);
tempIndex++;
countInBetween++;
flag = true;
}
if (flag) {
integerList.add(inputArray[index + countInBetween]);
integerList.add(inputArray[index - 1]);
int differnceBetweenHighest = min(integerList.get(integerList.size() - 2),
integerList.get(integerList.size() - 1));
int totalCapacity = differnceBetweenHighest * countInBetween;
totalWaterContained += totalCapacity - sum(integerList);
}
index += countInBetween + 1;
currentIndex = index - 1;
}
}
return totalWaterContained;
}
Here is my take to the problem,
I use a loop to see if the previous towers is bigger than the actual one.
If it is then I create another loop to check if the towers coming after the actual one are bigger or equal to the previous tower.
If that's the case then I just add all the differences in height between the previous tower and all other towers.
If not and if my loop reaches my last object then I simply reverse the array so that the previous tower becomes my last tower and call my method recursively on it.
That way I'm certain to find a tower bigger than my new previous tower and will find the correct amount of water collected.
public class towers {
public static int waterLevel(int[] i) {
int totalLevel = 0;
for (int j = 1; j < i.length - 1; j++) {
if (i[j - 1] > i[j]) {
for (int k = j; k < i.length; k++) {
if (i[k] >= i[j - 1]) {
for (int l = j; l < k; l++) {
totalLevel += (i[j - 1] - i[l]);
}
j = k;
break;
}
if (k == i.length - 1) {
int[] copy = Arrays.copyOfRange(i, j - 1, k + 1);
int[] revcopy = reverse(copy);
totalLevel += waterLevel(revcopy);
}
}
}
}
return totalLevel;
}
public static int[] reverse(int[] i) {
for (int j = 0; j < i.length / 2; j++) {
int temp = i[j];
i[j] = i[i.length - j - 1];
i[i.length - j - 1] = temp;
}
return i;
}
public static void main(String[] args) {
System.out.println(waterLevel(new int[] {1, 6, 3, 2, 2, 6}));
}
}
Tested all the Java solution provided, but none of them passes even half of the test-cases I've come up with, so there is one more Java O(n) solution, with all possible cases covered. The algorithm is really simple:
1) Traverse the input from the beginning, searching for tower that is equal or higher that the given tower, while summing up possible amount of water for lower towers into temporary var.
2) Once the tower found - add that temporary var into main result var and shorten the input list.
3) If no more tower found then reverse the remaining input and calculate again.
public int calculate(List<Integer> input) {
int result = doCalculation(input);
Collections.reverse(input);
result += doCalculation(input);
return result;
}
private static int doCalculation(List<Integer> input) {
List<Integer> copy = new ArrayList<>(input);
int result = 0;
for (ListIterator<Integer> iterator = input.listIterator(); iterator.hasNext(); ) {
final int firstHill = iterator.next();
int tempResult = 0;
int lowerHillsSize = 0;
while (iterator.hasNext()) {
final int nextHill = iterator.next();
if (nextHill >= firstHill) {
iterator.previous();
result += tempResult;
copy = copy.subList(lowerHillsSize + 1, copy.size());
break;
} else {
tempResult += firstHill - nextHill;
lowerHillsSize++;
}
}
}
input.clear();
input.addAll(copy);
return result;
}
For the test cases, please, take a look at this test class.
Feel free to create a pull request if you find uncovered test cases)
This is a funny problem, I just got that question in an interview. LOL I broke my mind on that stupid problem, and found a solution which need one pass (but clearly non-continuous). (and in fact you even not loop over the entire data, as you bypass the boundary...)
So the idea is. You start from the side with the lowest tower (which is now the reference). You directly add the content of the towers, and if you reach a tower which is highest than the reference, you call the function recursively (with side to be reset). Not trivial to explain with words, the code speak for himself.
#include <iostream>
using namespace std;
int compute_water(int * array, int index_min, int index_max)
{
int water = 0;
int dir;
int start,end;
int steps = std::abs(index_max-index_min)-1;
int i,count;
if(steps>=1)
{
if(array[index_min]<array[index_max])
{
dir=1;
start = index_min;
end = index_max;
}
else
{
dir = -1;
start = index_max;
end = index_min;
}
for(i=start+dir,count=0;count<steps;i+=dir,count++)
{
if(array[i]<=array[start])water += array[start] - array[i];
else
{
if(i<end)water += compute_water(array, i, end);
else water += compute_water(array, end, i);
break;
}
}
}
return water;
}
int main(int argc,char ** argv)
{
int size = 0;
int * towers;
if(argc==1)
{
cout<< "Usage: "<<argv[0]<< "a list of tower height separated by spaces" <<endl;
}
else
{
size = argc - 1;
towers = (int*)malloc(size*sizeof(int));
for(int i = 0; i<size;i++)towers[i] = atoi(argv[i+1]);
cout<< "water collected: "<< compute_water(towers, 0, size-1)<<endl;
free(towers);
}
}
I wrote this relying on some of the ideas above in this thread:
def get_collected_rain(towers):
length = len(towers)
acummulated_water=[0]*length
left_max=[0]*length
right_max=[0]*length
for n in range(0,length):
#first left item
if n!=0:
left_max[n]=max(towers[:n])
#first right item
if n!=length-1:
right_max[n]=max(towers[n+1:length])
acummulated_water[n]=max(min(left_max[n], right_max[n]) - towers[n], 0)
return sum(acummulated_water)
Well ...
> print(get_collected_rain([9,8,7,8,9,5,6]))
> 5
Here's my attempt in jQuery. It only scans to the right.
Working fiddle (with helpful logging)
var a = [1, 5, 3, 7, 2];
var water = 0;
$.each(a, function (key, i) {
if (i > a[key + 1]) { //if next tower to right is bigger
for (j = 1; j <= a.length - key; j++) { //number of remaining towers to the right
if (a[key+1 + j] >= i) { //if any tower to the right is bigger
for (k = 1; k < 1+j; k++) {
//add to water: the difference of the first tower and each tower between the first tower and its bigger tower
water += a[key] - a[key+k];
}
}
}
}
});
console.log("Water: "+water);
Here's my go at it in Python. Pretty sure it works but haven't tested it.
Two passes through the list (but deleting the list as it finds 'water'):
def answer(heights):
def accWater(lst,sumwater=0):
x,takewater = 1,[]
while x < len(lst):
a,b = lst[x-1],lst[x]
if takewater:
if b < takewater[0]:
takewater.append(b)
x += 1
else:
sumwater += sum(takewater[0]- z for z in takewater)
del lst[:x]
x = 1
takewater = []
else:
if b < a:
takewater.extend([a,b])
x += 1
else:
x += 1
return [lst,sumwater]
heights, swater = accWater(heights)
x, allwater = accWater(heights[::-1],sumwater=swater)
return allwater
private static int soln1(int[] a)
{
int ret=0;
int l=a.length;
int st,en=0;
int h,i,j,k=0;
int sm;
for(h=0;h<l;h++)
{
for(i=1;i<l;i++)
{
if(a[i]<a[i-1])
{
st=i;
for(j=i;j<l-1;j++)
{
if(a[j]<=a[i] && a[j+1]>a[i])
{
en=j;
h=en;
break;
}
}
if(st<=en)
{
sm=a[st-1];
if(sm>a[en+1])
sm=a[en+1];
for(k=st;k<=en;k++)
{
ret+=sm-a[k];
a[k]=sm;
}
}
}
}
}
return ret;
}
/*** Theta(n) Time COmplexity ***/
static int trappingRainWater(int ar[],int n)
{
int res=0;
int lmaxArray[]=new int[n];
int rmaxArray[]=new int[n];
lmaxArray[0]=ar[0];
for(int j=1;j<n;j++)
{
lmaxArray[j]=Math.max(lmaxArray[j-1], ar[j]);
}
rmaxArray[n-1]=ar[n-1];
for(int j=n-2;j>=0;j--)
{
rmaxArray[j]=Math.max(rmaxArray[j+1], ar[j]);
}
for(int i=1;i<n-1;i++)
{
res=res+(Math.min(lmaxArray[i], rmaxArray[i])-ar[i]);
}
return res;
}
Given an array A of N integers we draw N discs in a 2D plane, such that i-th disc has center in (0,i) and a radius A[i]. We say that k-th disc and j-th disc intersect, if k-th and j-th discs have at least one common point.
Write a function
int number_of_disc_intersections(int[] A);
which given an array A describing N discs as explained above, returns the number of pairs of intersecting discs. For example, given N=6 and
A[0] = 1
A[1] = 5
A[2] = 2
A[3] = 1
A[4] = 4
A[5] = 0
there are 11 pairs of intersecting discs:
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
so the function should return 11.
The function should return -1 if the number of intersecting pairs exceeds 10,000,000. The function may assume that N does not exceed 10,000,000.
O(N) complexity and O(N) memory solution.
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;
}
So you want to find the number of intersections of the intervals [i-A[i], i+A[i]].
Maintain a sorted array (call it X) containing the i-A[i] (also have some extra space which has the value i+A[i] in there).
Now walk the array X, starting at the leftmost interval (i.e smallest i-A[i]).
For the current interval, do a binary search to see where the right end point of the interval (i.e. i+A[i]) will go (called the rank). Now you know that it intersects all the elements to the left.
Increment a counter with the rank and subtract current position (assuming one indexed) as we don't want to double count intervals and self intersections.
O(nlogn) time, O(n) space.
Python 100 / 100 (tested) on codility, with O(nlogn) time and O(n) space.
Here is #noisyboiler's python implementation of #Aryabhatta's method with comments and an example.
Full credit to original authors, any errors / poor wording are entirely my fault.
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
Worked example: given [3, 0, 1, 6] the disk radii would look like this:
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),
Well, I adapted Falk Hüffner's idea to c++, and made a change in the range.
Opposite to what is written above, there is no need to go beyond the scope of the array (no matter how large are the values in it).
On Codility this code received 100%.
Thank you Falk for your great idea!
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;
}
This can even be done in linear time [EDIT: this is not linear time, see comments]. In fact, it becomes easier if you ignore the fact that there is exactly one interval centered at each point, and just treat it as a set of start- and endpoints of intervals. You can then just scan it from the left (Python code for simplicity):
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
Here's a O(N) time, O(N) space algorithm requiring 3 runs across the array and no sorting, verified scoring 100%:
You're interested in pairs of discs. Each pair involves one side of one disc and the other side of the other disc. Therefore we won't have duplicate pairs if we handle one side of each disc. Let's call the sides right and left (I rotated the space while thinking about it).
An overlap is either due to a right side overlapping another disc directly at the center (so pairs equal to the radius with some care about the array length) or due to the number of left sides existing at the rightmost edge.
So we create an array that contains the number of left sides at each point and then it's a simple sum.
C code:
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;
}
I got 100 out of 100 with this C++ implementation:
#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;
}
A Python answer
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
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;
}
}
I'm offering one more solution because I did not find the counting principle of the previous solutions easy to follow. Though the results are the same, an explanation and more intuitive counting procedure seems worth presenting.
To begin, start by considering the O(N^2) solution that iterates over the discs in order of their center points, and counts the number of discs centered to the right of the current disc's that intersect the current disc, using the condition current_center + radius >= other_center - radius. Notice that we could get the same result counting discs centered to the left of the current disc using the condition 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
We can go from O(N^2) to O(N) if we could only "look up" the number of discs to the right (or to the left!) that intersect the current disc. The key insight is to reinterpret the intersection condition as "the right edge of one disc overlaps the left edge of another disc", meaning (a ha!) the centers don't matter, only the edges.
The next insight is to try sorting the edges, taking O(N log N) time. Given a sorted array of the left edges and a sorted array of the right edges, as we scan our way from left to right along the number line, the number of left or right edges to the left of the current location point is simply the current index into left_edges and right_edges respectively: a constant-time deduction.
Finally, we use the "right edge > left edge" condition to deduce that the number of intersections between the current disc and discs that start only to the left of the current disc (to avoid duplicates) is the number of left edges to the left of the current edge, minus the number of right edges to the left of the current edge. That is, the number of discs starting to left of this one, minus the ones that closed already.
Now for this code, tested 100% on Codility:
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
Java 2*100%.
result is declared as long for a case codility doesn't test, namely 50k*50k intersections at one 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;
}
}
Swift 4 Solution 100% (Codility do not check the worst case for this 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
}
Here my JavaScript solution, based in other solutions in this thread but implemented in other languages.
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;
}
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++;
}
}
It is O(N^2) so pretty slow, but it works.
This is a ruby solution that scored 100/100 on codility. I'm posting it now because I'm finding it difficult to follow the already posted ruby answer.
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
Probably extremely fast. O(N). But you need to check it out. 100% on Codility.
Main idea:
1. At any point of the table, there are number of circles "opened" till the right edge of the circle, lets say "o".
2. So there are (o-1-used) possible pairs for the circle in that point. "used" means circle that have been processed and pairs for them counted.
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;
}
There are a lot of great answers here already, including the great explanation from the accepted answer. However, I wanted to point out a small observation about implementation details in the Python language.
Originally, I've came up with the solution shown below. I was expecting to get O(N*log(N)) time complexity as soon as we have a single for-loop with N iterations, and each iteration performs a binary search that takes at most 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
However, I've get O(N**2) and a timeout failure. Do you see what is wrong here? Right, this line:
pos = bisect.bisect_right(lefts[i+1:], r)
In this line, you are actually taking a copy of the original list to pass it into binary search function, and it totally ruins the efficiency of the proposed solution! It makes your code just a bit more consice (i.e., you don't need to write pos - i - 1) but heavily undermies the performance. So, as it was shown above, the solution should be:
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
It seems that sometimes one could be too eager about making slices and copies because Python allows you to do it so easily :) Probably not a great insight, but for me it was a good lesson to pay more attention to these "technical" moments when converting ideas and algorithms into real-word solutions.
I know that this is an old questions but it is still active on codility.
private int solution(int[] A)
{
int openedCircles = 0;
int intersectCount = 0;
We need circles with their start and end values. For that purpose I have used Tuple.
True/False indicates if we are adding Circle Starting or Circle Ending value.
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));
}
Order "circles" by their values.
If one circle is ending at same value where other circle is starting, it should be counted as intersect (because of that "opening" should be in front of "closing" if in same point)
circles = circles.OrderBy(x => x.Item1).ThenByDescending(x => x.Item2).ToList();
Counting and returning counter
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;
}
Javascript solution 100/100 based on this video https://www.youtube.com/watch?v=HV8tzIiidSw
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
}
so, I was doing this test in Scala and I would like to share here my example. My idea to solve is:
Extract the limits to the left and right of each position on the array.
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)
Check if there is intersections between any two positions
(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
if any of these two checks is true count one intersection.
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
}
}
This got 100/100 in 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));
}
}
}
Thanks to Falk for the great idea! Here is a ruby implementation that takes advantage of sparseness.
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]
#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;
}
An Implementation of Idea stated above in 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);
}
}
Here is the PHP code that scored 100 on codility:
$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;
However I dont understand the logic. This is just transformed C++ code from above. Folks, can you elaborate on what you were doing here, please?
A 100/100 C# implementation as described by Aryabhatta (the binary search solution).
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;
}
}
100% score in Codility.
Here is an adaptation to C# of Толя 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;
}
Here's a two-pass C++ solution that doesn't require any libraries, binary searching, sorting, 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;
}
My answer in Swift; gets a 100% score.
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
}
C# solution 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;
}
}
Given an array A of N integers we draw N discs in a 2D plane, such that i-th disc has center in (0,i) and a radius A[i]. We say that k-th disc and j-th disc intersect, if k-th and j-th discs have at least one common point.
Write a function
int number_of_disc_intersections(int[] A);
which given an array A describing N discs as explained above, returns the number of pairs of intersecting discs. For example, given N=6 and
A[0] = 1
A[1] = 5
A[2] = 2
A[3] = 1
A[4] = 4
A[5] = 0
there are 11 pairs of intersecting discs:
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
so the function should return 11.
The function should return -1 if the number of intersecting pairs exceeds 10,000,000. The function may assume that N does not exceed 10,000,000.
O(N) complexity and O(N) memory solution.
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;
}
So you want to find the number of intersections of the intervals [i-A[i], i+A[i]].
Maintain a sorted array (call it X) containing the i-A[i] (also have some extra space which has the value i+A[i] in there).
Now walk the array X, starting at the leftmost interval (i.e smallest i-A[i]).
For the current interval, do a binary search to see where the right end point of the interval (i.e. i+A[i]) will go (called the rank). Now you know that it intersects all the elements to the left.
Increment a counter with the rank and subtract current position (assuming one indexed) as we don't want to double count intervals and self intersections.
O(nlogn) time, O(n) space.
Python 100 / 100 (tested) on codility, with O(nlogn) time and O(n) space.
Here is #noisyboiler's python implementation of #Aryabhatta's method with comments and an example.
Full credit to original authors, any errors / poor wording are entirely my fault.
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
Worked example: given [3, 0, 1, 6] the disk radii would look like this:
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),
Well, I adapted Falk Hüffner's idea to c++, and made a change in the range.
Opposite to what is written above, there is no need to go beyond the scope of the array (no matter how large are the values in it).
On Codility this code received 100%.
Thank you Falk for your great idea!
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;
}
This can even be done in linear time [EDIT: this is not linear time, see comments]. In fact, it becomes easier if you ignore the fact that there is exactly one interval centered at each point, and just treat it as a set of start- and endpoints of intervals. You can then just scan it from the left (Python code for simplicity):
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
Here's a O(N) time, O(N) space algorithm requiring 3 runs across the array and no sorting, verified scoring 100%:
You're interested in pairs of discs. Each pair involves one side of one disc and the other side of the other disc. Therefore we won't have duplicate pairs if we handle one side of each disc. Let's call the sides right and left (I rotated the space while thinking about it).
An overlap is either due to a right side overlapping another disc directly at the center (so pairs equal to the radius with some care about the array length) or due to the number of left sides existing at the rightmost edge.
So we create an array that contains the number of left sides at each point and then it's a simple sum.
C code:
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;
}
I got 100 out of 100 with this C++ implementation:
#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;
}
A Python answer
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
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;
}
}
I'm offering one more solution because I did not find the counting principle of the previous solutions easy to follow. Though the results are the same, an explanation and more intuitive counting procedure seems worth presenting.
To begin, start by considering the O(N^2) solution that iterates over the discs in order of their center points, and counts the number of discs centered to the right of the current disc's that intersect the current disc, using the condition current_center + radius >= other_center - radius. Notice that we could get the same result counting discs centered to the left of the current disc using the condition 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
We can go from O(N^2) to O(N) if we could only "look up" the number of discs to the right (or to the left!) that intersect the current disc. The key insight is to reinterpret the intersection condition as "the right edge of one disc overlaps the left edge of another disc", meaning (a ha!) the centers don't matter, only the edges.
The next insight is to try sorting the edges, taking O(N log N) time. Given a sorted array of the left edges and a sorted array of the right edges, as we scan our way from left to right along the number line, the number of left or right edges to the left of the current location point is simply the current index into left_edges and right_edges respectively: a constant-time deduction.
Finally, we use the "right edge > left edge" condition to deduce that the number of intersections between the current disc and discs that start only to the left of the current disc (to avoid duplicates) is the number of left edges to the left of the current edge, minus the number of right edges to the left of the current edge. That is, the number of discs starting to left of this one, minus the ones that closed already.
Now for this code, tested 100% on Codility:
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
Java 2*100%.
result is declared as long for a case codility doesn't test, namely 50k*50k intersections at one 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;
}
}
Swift 4 Solution 100% (Codility do not check the worst case for this 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
}
Here my JavaScript solution, based in other solutions in this thread but implemented in other languages.
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;
}
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++;
}
}
It is O(N^2) so pretty slow, but it works.
This is a ruby solution that scored 100/100 on codility. I'm posting it now because I'm finding it difficult to follow the already posted ruby answer.
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
Probably extremely fast. O(N). But you need to check it out. 100% on Codility.
Main idea:
1. At any point of the table, there are number of circles "opened" till the right edge of the circle, lets say "o".
2. So there are (o-1-used) possible pairs for the circle in that point. "used" means circle that have been processed and pairs for them counted.
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;
}
There are a lot of great answers here already, including the great explanation from the accepted answer. However, I wanted to point out a small observation about implementation details in the Python language.
Originally, I've came up with the solution shown below. I was expecting to get O(N*log(N)) time complexity as soon as we have a single for-loop with N iterations, and each iteration performs a binary search that takes at most 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
However, I've get O(N**2) and a timeout failure. Do you see what is wrong here? Right, this line:
pos = bisect.bisect_right(lefts[i+1:], r)
In this line, you are actually taking a copy of the original list to pass it into binary search function, and it totally ruins the efficiency of the proposed solution! It makes your code just a bit more consice (i.e., you don't need to write pos - i - 1) but heavily undermies the performance. So, as it was shown above, the solution should be:
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
It seems that sometimes one could be too eager about making slices and copies because Python allows you to do it so easily :) Probably not a great insight, but for me it was a good lesson to pay more attention to these "technical" moments when converting ideas and algorithms into real-word solutions.
I know that this is an old questions but it is still active on codility.
private int solution(int[] A)
{
int openedCircles = 0;
int intersectCount = 0;
We need circles with their start and end values. For that purpose I have used Tuple.
True/False indicates if we are adding Circle Starting or Circle Ending value.
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));
}
Order "circles" by their values.
If one circle is ending at same value where other circle is starting, it should be counted as intersect (because of that "opening" should be in front of "closing" if in same point)
circles = circles.OrderBy(x => x.Item1).ThenByDescending(x => x.Item2).ToList();
Counting and returning counter
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;
}
Javascript solution 100/100 based on this video https://www.youtube.com/watch?v=HV8tzIiidSw
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
}
so, I was doing this test in Scala and I would like to share here my example. My idea to solve is:
Extract the limits to the left and right of each position on the array.
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)
Check if there is intersections between any two positions
(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
if any of these two checks is true count one intersection.
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
}
}
This got 100/100 in 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));
}
}
}
Thanks to Falk for the great idea! Here is a ruby implementation that takes advantage of sparseness.
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]
#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;
}
An Implementation of Idea stated above in 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);
}
}
Here is the PHP code that scored 100 on codility:
$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;
However I dont understand the logic. This is just transformed C++ code from above. Folks, can you elaborate on what you were doing here, please?
A 100/100 C# implementation as described by Aryabhatta (the binary search solution).
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;
}
}
100% score in Codility.
Here is an adaptation to C# of Толя 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;
}
Here's a two-pass C++ solution that doesn't require any libraries, binary searching, sorting, 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;
}
My answer in Swift; gets a 100% score.
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
}
C# solution 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;
}
}