Description
Master Locksmith has just finished the work of his life: a combination lock so big and complex that no one will ever open it without knowing the right combination. He's done testing it, so now all he has to do is put the lock back into a neutral state, as to leave no trace of the correct combination.
The lock consists of some number of independently rotating discs. Each disc has k numbers from
0
tok-1
written around its circumference. Consecutive numbers are adjacent to each other, i.e.1
is between0
and2
, and because the discs are circular,0
is betweenk-1
and1
. Discs can rotate freely in either direction. On the front of the lock there's a marked bar going across all discs to indicate the currently entered combination.Master Locksmith wants to reset the lock by rotating discs until all show the same number. However, he's already itching to start work on an even greater and more complicated lock, so he's not willing to waste any time: he needs to choose such a number that it will take as little time as possible to reach it. He can only rotate one disc at a time, and it takes him
1
second to rotate it by one position. Givenk
and theinitialState
of the lock, find the number he should choose. If there are multiple possible answers, return the smallest one.
Example
For
k = 10
andinitialState = [2, 7, 1]
the output should be
masterLocksmith(k, initialState) = 1
It takes 1 second for the first disc to reach
1 (2 → 1)
. It takes 4 seconds for the second disc to reach1 (7 → 8 → 9 → 0 → 1)
. The third disc is already at1
. The whole process can be completed in5
seconds. Reaching any other number would take longer, so this is the optimal solution.
Constraints
Guaranteed constraints:
3 ≤ k ≤ 10 ** 14
2 ≤ initialState.length ≤ 10**4
0 ≤ initialState[i] < k for all valid i
Code
from operator import itemgetter
from collections import defaultdict
def masterLocksmith(k, initial_state):
occurence = defaultdict(int)
for i in initial_state:
occurence[i] += 1
num_set = set(initial_state)
num_set.add(0)
shortest = {j: 0 for j in num_set}
for i in occurence:
for j in num_set:
shortest[j] += min(abs(i-j), k - abs(i-j)) * occurence[i]
return min([[k, shortest[k]] for k in shortest], key=itemgetter(1,0))[0]
The TLE's were really hard, I've tried some optimization but the best I could solve this in was still \$ O(n * k) \$ which was not enough to complete the challenge. I am less concerned about readability but more how this can be sped up.