My solution is O(N)+O(X/D) . I have two arguments (well, an excuse and a real argument) to protect it as O(N) :
The apology is that I must have O(X) space, and I can't even initialize it to O(N) . Therefore, I assume that the array is pre-initialized, and since my O(X/D) is only the initialization of the array by default, I will gladly ignore it. (Hi, I said that was an excuse).
The real argument is that X/D cannot really be greater than N I mean, if I need to move X positions in steps of no more than D , then each minimum number of steps will be X/D (which means X/D-1 tiles).
- Thus, any problem with
X/D-1 > N unsolvable. - So, the algorithm can start with
if (X/D > N+1) return -1 . - So
O(X/D) never greater than O(N) . - So,
O(N)+O(X/D) actually coincides with O(N) .
However, I go with my solution:
Math setting
I will take the “track” with positions 0 to X , so 0 is on the left and X on the right (I need this because I will talk about the “leftmost tile” and the like). The track has X+1 positions numbered from 0 to X First there is a tile at 0 , and the other at X
I split the track into pieces. The fragment size is such that any two adjacent fragments are exactly D The first piece is k , the second is Dk , the third is k , the fourth is Dk again, etc., Moreover, k is something between 1 and D-1 . If D even and we set k=D/2 , all pieces will be equal in size. I feel that the implementation may be a little simpler if k set to 1 and the fragments are processed in pairs, but I really don't know (I did not implement this), and the algorithm is basically the same for any k so I will move on.
The last fragment may be truncated, but I just assume that this is the size that should be, even if it means that it goes beyond X It does not matter. For example, if X=30 , D=13 , k=6 , then there will be 5 pieces with sizes 6-7-6-7-6 (i.e. 0-5 , 6-12 , 13-18 , 19-24 , 25-31 , at 31 , which is not part of the track).
From now on I will use the notation of the array for fragments, i.e. I will refer to the fragment number k as C[k] .
It is very important that two adjacent fragments are always added exactly at position D , because it ensures that:
- If each piece has at least a tile on it, the problem is solved (i.e. no more tiles are needed).
- If there are no tiles on two adjacent pieces, more tiles are required.
If there is a piece that does not fall into any of the previous cases (i.e. one that does not have tiles in it, but the previous and subsequent pieces have tiles), then we need to measure the distance between the leftmost plate in the piece to the right, and the rightmost tile in the piece to the left. If this distance is less than or equal to D , then this piece is not a problem.
To summarize: some pieces are problematic and some are not consistent, according to the following rules:
- A piece with at least one tile on it is never problematic.
- A piece without tiles, and a neighbor (left, right, or both), which also has no tiles, is always problematic.
- A piece
C[k] without fragments, with neighbors C[k-1] and C[k+1] with fragments, is problematic if and only if C[k+1].left - C[k-1].right > D
And, the part that jumps into solving the problem:
- We need more fragments to complete the track, if and only if there is at least one problem fragment.
So, the problem regarding the positions of O(X) now deals with no more than O(N) pieces. It's great.
Implementation Details
In an array of pieces, each piece of C[k] will have the following attributes:
- boolean
problematic initialized to true . - integer
left initialized to -1 . - integer
right initialized to -1 .
In addition, there will be a problematicCounter initialized by the number of elements in the C array. It will be ticking down when it reaches zero, we know that we no longer need tiles.
The algorithm is as follows:
if (X/D > N+1) return -1; // Taking care of rounding is left as an exercise Let C = array of chunks as described above For each C[k] // This is the O(X/D) part { Let C[k].problematic = true Let C[k].left = -1 Let C[k].right = -1 } Let problematicCounter = number of elements in array C Let C[k] be the chunk that contains position 0 (usually the first one, but I'll leave open the possibility of "sentinel" chunks) Let C[k].problematic = false Let C[k].left = 0 Let C[k].right = 0 Decrement problematicCounter // The following steps would require tweaking if there is one single chunk on the track // I do not consider that case as that would imply D >= 2*N, which is kind of ridiculous for this problem Let C[k] be the chunk containing position X (the last position on the track) Let C[k].problematic = false Let C[k].left = X Let C[k].right = X Decrease problematicCounter // Initialization done. Now for the loop. // Everything inside the loop is O(1), so the loop itself is O(N) For each A[i] in array A (the array specifying which tile to place in second i) { Let C[k] be the chunk containing position A[i] If C[k].problematic == true { Let C[k].problematic = false; Decrement problematicCounter } If C[k].first == -1 OR C[k].first > A[i] { Let C[k].first = A[i] // Checks that C[k-1] and C[k-2] don't go off array index bounds left as an exercise If C[k-1].problematic == true AND C[k-2].last <> -1 AND C[k].first - C[k-2].last <= D { Let C[k-1].problematic = false Decrement problematicCounter } If C[k].last == -1 OR C[k].last < A[i] { Let C[k].last = A[i] // Checks that C[k+1] and C[k+2] don't go off array index bounds left as an exercise If C[k+1].problematic == true AND C[k+2].first <> -1 AND C[k+2].first - C[k].last <= D { Let C[k+1].problematic = false Decrement problematicCounter } If problematicCounter == 0 Then return k // and forget everything else } return -1