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 an assignment to solve using dynamic programming the following problem:
There is a rectangular sheet and a set of rectangular elements of given dimensions and value. The task is to divide the sheet into elements of given dimensions, so that the sum of values of the elements is maximum. Find this sum and a tree of consequent cuts.
There are following conditions:
It is NOT possible to rotate the given elements.
It is possible to cut out unlimited number of certain types of
elements.
It is possible that some parts of the sheet will remain unused.
The only possible way to cut the sheet is by a straight
line, so that you again obtain two smaller rectangles.
The problem is solved. Solution can be found below.
==========================================================================
I understand the problem for one dimension, which comes to the rod cutting problem. You divide the rod into the smallest possible pieces, take the first one and check if you can build it with the given segments. Remember the weight you'll get with building the part this way and move on to a bigger part containing the previous one. You go back by the length of the segment you're trying at the moment and check if using this segment plus the weight of the previously build part will make up to better sum of the weight for the current part.
Supposedly, the cutting wood problem is no different, but you add the 2-dimension, additional loop somewhere in the middle. Unfortunately, I can't imagine how to store the values and how to go back for the 2-dimensions.
I've tried doing like:
1. Loop on one dimension
2. Loop on second dimension
3. Loop on all the segments you can use
4. Check if you can fit the current segment depending on 1. and 2.
5. If yes, go back the length of the segment to see if weight of the segment + what's stored there gives you a greater result; do the same for the width
6. Store the result in the cell you're currently on
7. Go through the array and find the greatest result
Here is the code I produced after many debugging tries:
public int Cut((int length, int width) sheet, (int length, int width, int price)[] elements, out Cut cuts)
{
int[,] tmpSheetArr = new int[sheet.length + 1, sheet.width + 1];
for (int i = 1; i < tmpSheetArr.GetLength(0); i++)
{
for (int j = 1; j < tmpSheetArr.GetLength(1); j++)
{
tmpSheetArr[i, j] = Int32.MinValue;
}
}
for (int i = 1; i < tmpSheetArr.GetLength(0); i++) //columns
{
for (int j = 1; j < tmpSheetArr.GetLength(1); j++) //rows
{
for (int e = 0; e < elements.Length; e++)
{
(int length, int width, int price) elem = elements[e];
if (i >= elem.length && j >= elem.width)
{
int tmpJ, tmpI, tmpVal;
tmpJ = j - elem.width;
tmpI = i;
while (0 < tmpI)
{
if(tmpI > i - elem.length && tmpI <= i && tmpJ > j - elem.width && tmpJ <= j)
{
tmpJ -= 1;
if (-1 == tmpJ)
{
tmpJ = tmpSheetArr.GetLength(1) - 1;
tmpI -= 1;
}
continue;
}
tmpVal = tmpSheetArr[tmpI, tmpJ] == Int32.MinValue ? 0 : tmpSheetArr[tmpI, tmpJ];
if (tmpSheetArr[i, j] < elem.price + tmpVal)
{
tmpSheetArr[i, j] = elem.price + tmpVal;
}
tmpJ -= 1;
if(-1 == tmpJ)
{
tmpJ = tmpSheetArr.GetLength(1) - 1;
tmpI -= 1;
}
}
}
}
}
}
int tmpMax = 0;
for (int i = 1; i < tmpSheetArr.GetLength(0); i++)
{
for (int j = 1; j < tmpSheetArr.GetLength(1); j++)
{
if (tmpMax < tmpSheetArr[i, j])
tmpMax = tmpSheetArr[i, j];
}
}
cuts = null;
return tmpMax;
}
It doesn't work, gives too big results in some cases and gets stuck on bigger problems. I think the main problem is about going back - with only the weight stored I don't know what size of the block was used and if it will overlap with the current one.
I decided to write it from the beginning, but really can't find another approach. I have a code for the 1D problem:
int cutRod(int[] price, int n)
{
int[] val = new int[n + 1];
val[0] = 0;
int i, j;
// Build the table val[] and return the last entry
// from the table
for (i = 1; i <= n; i++)
{
int max_val = Int32.MinValue;
for (j = 0; j < i; j++)
max_val = Math.Max(max_val, price[j] + val[i - j - 1]);
val[i] = max_val;
}
return val[n];
}
How do I change it so it works for 2D problem?
I tried to explain my limited understanding and way of thinking the best I could. I would appreciate any help on this matter.
Make your dynamic state at x be a dictionary mapping a particular "skyline" of what blocks placed before x look like after x. You start with a flat skyline (no blocks so far, clean edge), and you're looking for a flat skyline at the other end (didn't go off the edge of the sheet).
As you advance you "lower" your skyline by 1, start looking at ways to cut out new blocks, and get new possible skylines.
The number of possible skylines will grow exponentially with the width of the rectangle.
The solution:
Build an array of maximum values that can be obtained from given piece of dimensions 1x1 up to the size of the board. Maximum value for given piece is stored under index of [(length of the piece) - 1, (width of the piece) - 1]. To find the maximum value, check how the current piece can be formed with previous pieces and cuts.
To construct the tree of cuts, build a second array of the best cuts for the current piece. Root of the cuts tree for the current piece is stored under index of [(length of the piece) - 1, (width of the piece) - 1].
Cuts class:
public class Cut
{
public int length; // vertical dimension (before cut)
public int width; // horizontal dimension (before cut)
public int price; // sum of the values of the two elements resulting from the cut
public bool vertical; // true for vertical cut, false otherwise
public int n; // distance from left side (for vertical cut) or top (for horizontal cut) of the current piece
// price 0 means there was no cut, topleft and bottomright are null,
public Cut topleft; // top/left resulting piece after cut
public Cut bottomright; // bottom/right resulting piece after cut
public Cut(int length, int width, int price, bool vertical=true, int n=0, Cut topleft=null, Cut bottomright=null)
{
this.length = length;
this.width = width;
this.price = price;
this.vertical = vertical;
this.n = n;
this.topleft = topleft;
this.bottomright = bottomright;
}
}
Function finding the maximum value and a tree of cuts:
public int Cut((int length, int width) sheet, (int length, int width, int price)[] elements, out Cut cuts)
{
int[,] sheetArr = new int[sheet.length, sheet.width]; //contains best values of current pieces that can be formed
Cut[,] cutsArr = new Cut[sheet.length, sheet.width]; //contains references for cuts used to form pieces of the best value,
for (int l = 0; l < sheet.length; l++) //loop on length
{
for (int w = 0; w < sheet.width; w++) //loop on width
{
foreach ((int length, int width, int price) elem in elements) //loop on elements
{
if (elem.length == l + 1 && elem.width == w + 1) //check if current piece can be build with one of the given elements
{
sheetArr[l, w] = elem.price;
cutsArr[l, w] = new Cut(elem.length, elem.width, elem.price); //piece is exactly one of the elements (no cut)
break; //no 2 elements of the same size in the given elements
}
cutsArr[l, w] = new Cut(l + 1, w + 1, 0); //piece can not be formed from given elements, price = 0 (no cut)
}
for (int i = 1; i < Math.Floor((decimal)(l + 1) / 2) + 1; i++) //go back on length
{
if (sheetArr[i - 1, w] + sheetArr[l - i, w] > sheetArr[l, w])
{
sheetArr[l, w] = sheetArr[i - 1, w] + sheetArr[l - i, w];
cutsArr[l, w] = new Cut(l + 1, w + 1, sheetArr[l, w], false, i, cutsArr[i - 1, w], cutsArr[l - i, w]);
}
}
for (int i = 1; i < Math.Floor((decimal)(w + 1) / 2) + 1; i++) //go back on width
{
if (sheetArr[l, i - 1] + sheetArr[l, w - i] > sheetArr[l, w])
{
sheetArr[l, w] = sheetArr[l, i - 1] + sheetArr[l, w - i];
cutsArr[l, w] = new Cut(l + 1, w + 1, sheetArr[l, w], true, i, cutsArr[l, i - 1], cutsArr[l, w - i]);
}
}
}
}
cuts = cutsArr[sheet.length - 1, sheet.width - 1];
return sheetArr[sheet.length - 1, sheet.width - 1];
}
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;
}
}
I have been trying to solve the following problem in interview street. Count Scorecards(30 points)
In a tournament, N players play against each other exactly once. Each game results in either of the player winning. There are no ties. You have given a scorecard containing the scores of each player at the end of the tournament. The score of a player is the total number of games the player won in the tournament. However, the scores of some players might have been erased from the scorecard. How many possible scorecards are consistent with the input scorecard?
Input:
The first line contains the number of cases T. T cases follow. Each case contains the number N on the first line followed by N numbers on the second line. The ith number denotes s_i, the score of the ith player. If the score of the ith player has been erased, it is represented by -1.
Output:
Output T lines, containing the answer for each case. Output each result modulo 1000000007.
Constraints:
1 <= T <= 20
1 <= N <= 40
-1 <= s_i < N
Sample Input:
5
3
-1 -1 2
3
-1 -1 -1
4
0 1 2 3
2
1 1
4
-1 -1 -1 2
Sample Output:
2
7
1
0
12
Explanation:
For the first case, there are 2 scorecards possible: 0,1,2 or 1,0,2.
For the second case, the valid scorecards are 1,1,1, 0,1,2, 0,2,1, 1,0,2, 1,2,0, 2,0,1, 2,1,0.
For the third case, the only valid scorecard is {0,1,2,3}.
For the fourth case, there is no valid scorecard. It is not possible for both players to have score 1.
I have tried to come up with generic functions approach, but i am really trying to nail down this problem using Dynamic programming. How can you think of recurrence relations for this problem?.
Here is the DP solution to the above problem
public static int[][] table; // stores the result of the overlapping sub problems
private static int N;
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int testCases = scanner.nextInt();
for (int i = 0; i < testCases; i++) {
N = scanner.nextInt();
int[] scores = new int[N];
for (int j = 0; j < N; j++) {
scores[j] = scanner.nextInt();
}
long result = process(scores) % 1000000007L;
System.out.println(result );
}
}
private static long process(int[] scores) {
int sum = 0;
int amongPlayers = 0; //count no of players whose score has been erased(-1)
for (int i = 0; i < N; i++) {
if (scores[i] != -1) {
sum += scores[i];
} else {
amongPlayers++;
}
}
int noGames = (N * (N -1)) /2; // total number of games
if (sum < noGames) {
int distribute = noGames - sum; // score needed to be distributed;
table = new int[distribute + 1 ][amongPlayers + 1];
for (int m = 0; m <= distribute; m++) {
for (int n = 0; n <= amongPlayers; n++) {
table[m][n] = -1;
}
}
return distribute(distribute, amongPlayers); // distrubute scores among players whose score is erased(-1)
}
else if(sum == noGames){
return 1;
}
return 0;
}
/**
* Dynamic programming recursive calls
* #param distribute
* #param amongPlayers
* #return
*/
private static int distribute(int distribute, int amongPlayers) {
if(distribute == 0 && amongPlayers == 0)
return 1;
if (amongPlayers <= 0)
return 0;
if(distribute == 0)
return 1;
int result = 0;
if (table[distribute][amongPlayers - 1] == -1) {
int zeroResult = distribute(distribute, amongPlayers - 1);
table[distribute][amongPlayers - 1] = zeroResult;
}
result += table[distribute][amongPlayers - 1];
for (int i = 1; i < N ; i++) { // A person could win maximum of N-1 games
if (distribute - i >= 0) {
if (table[distribute - i][amongPlayers - 1] == -1) {
int localResult = distribute(distribute - i,
amongPlayers - 1);
table[distribute - i][amongPlayers - 1] = localResult;
}
result += table[distribute - i][amongPlayers - 1];
}
}
return result;
}
Observations:
Sequence s[1], s[2], ..., s[n] to be consistent scorecard, these properties must hold:
s[i1] + s[i2] + .. + s[ik] >= k * (k — 1) / 2, where i1 < i2 < .. < ik (i.e for every subsequences of length k)
s[1] + s[2] + .. + s[n] = n * (n — 1) / 2
First of all we need to check not erased scores, just using 1 condition. Then put erased scores using dynamic programming.
Let's denote erased scores b[i], not erased scores a[i];
sum{i = 1 .. l} a[i] + sum{i = 1 .. k} b[i] >= (k + l) * (k + l - 1) / 2
sum{i = 1 .. l} a[i] + sum{i = 1 .. k} b[i] >= 0 + 1 + .. + (k + l - 1)
sum{i = 1 .. l} (a[i] - (k + i - 1)) + sum{i = 1 .. k} b[i] >= 0 + 1 + .. + (k - 1)
So we can pre calculate for every k, minimal value of sum{i = 1 .. l} (a[i] - (k + i - 1))/
Dynamic programming:
states:
dp[k][score][sum]: we know first k minimum erased scores, and their values not exceeds $score$, and sum is their sum.
transitions:
Skip score, dp[k][score][sum] += dp[k][score + 1][sum];
Put $i$ scores of value $score$ dp[k][score][sum] += C[m — k][i] * dp[k + i][score + 1][sum + i*score], where m number of erased scores, C[n][k] = combination.
my code
The total sum of the wins should be (N C 2)
Subtract the known values which are given in the input. Let the remaining sum (N C 2) - x be called S. Let the number of -1's in the input be Q.
The problem now boils down to finding the number of integral solutions of Q variables ranging from 0 to N-1 (max score possible) and sum of which is S
Let DP[q][s] denote the number of integral solutions of q variables whose sum is s
Then we have,
DP[q][s] = Sum (i=0 to N-1) DP[q-1][s-i]
DP[Q][S] gives the solution
EDIT:
Observation:
For x people remaining, the number of total wins should be at least x*(x-1)/2 (when they play each other). Thus, at any time for q people, s cannot exceed (N-q)(N-q-1)/2 = M
There should be one more constraint that DP[q][s] should be equal to 0 when s is greater than M
I'm trying to solve this assignment, too, and think it should be something like this:
The number of players (=N), the number of unknown cards (count the "-1") and the sum of the known cards (count all cards except "-1") are given. The total number of games possible should be 1 +2 +3 + ... + (players-1): The first player has (players-1) opponents, the second player (players-2) etc.
Now you can recursively calculate the sum of possible score cards:
Initialize an empty hashmap with (players, unknown cards, sum of known cards) as the key and the sum of possible score cards as the value.
If all cards are defined, then the answer is either 0 (if the sum of all cards equals the total number of games possible) or 1 (if the sum of all cards does not equal the total number of games possible).
If not all cards are defined, then run a for loop and set one unknown card to 0, 1, 2 ... (players-1) and try to read the result from the hashmap. If it is not in the hashmap call the method itself and save the result in the map.
The recursion code should be something like this:
def recursion(players: Int, games: Int, unknownCards: Int, knownScore: Int): Int = {
unknownCards match {
case 0 if knownScore != games => 0
case 0 if knownScore == games => 1
case _ =>
map.get(players, unknownCards, knownScore) getOrElse {
var sum = 0
for (i <- 0 until players) sum += main(players, games, unknownCards - 1, knownScore + i)
sum %= 1000000007
map.put((players, unknownCards, knownScore), sum)
sum
}
}
}
Try this
import java.util.Scanner;
public class Solution {
final private static int size = 780;
private static long[][] possibleSplits = new long[size][size];
static {
for(int i=0; i < size; ++i)
possibleSplits[i][0] = 1;
for(int j=0; j< size; ++j)
possibleSplits[0][j] = j+1;
for(int i=1; i< size; ++i)
for(int j=1; j < size; ++j)
{
possibleSplits[i][j] = (possibleSplits[i-1][j] + possibleSplits[i][j-1]) % 1000000007;
}
}
public long possibleWays = 0;
public Solution(int n, String scores)
{
long totalScores = 0;
int numOfErasedScores = 0;
for(String str : scores.split(" "))
{
int s = Integer.parseInt(str);
if (s < 0)
++numOfErasedScores;
else
totalScores += s;
}
long totalErasedScores = ncr(n,2) - totalScores;
if(totalErasedScores == 0)
++possibleWays;
else if (totalErasedScores > 0)
partition(n-1, totalErasedScores, numOfErasedScores);
}
private void partition(int possibleMax, long total, int split)
{
if (split == 0)
return;
possibleWays = possibleSplits[(int)total-1][split-1];
if (total > possibleMax)
possibleWays -= split;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int numberOfTestCases = Integer.parseInt(in.nextLine().trim());
for(int i=0; i< numberOfTestCases; ++i)
{
String str = in.nextLine().trim();
int numberOfPlayers = Integer.parseInt(str);
String playerScores = in.nextLine().trim();
long result = new Solution(numberOfPlayers, playerScores).possibleWays;
System.out.println(result % 1000000007);
}
in.close();
}
public static long ncr(int n, int r)
{
long result = 1;
for(int i= Math.max(n-r, r)+1;i<=n;++i)
result*= i;
result/= fact(Math.min(n-r,r));
return result;
}
public static long fact(int n)
{
long result = 1;
for(int i =2; i<= n; ++i)
result *= i;
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;
}
}