|
| 1 | +// Runtime: 100 ms (Top 85.48%) | Memory: 18.80 MB (Top 11.29%) |
| 2 | + |
1 | 3 | class Solution:
|
2 |
| - def numMovesStonesII(self, stones: list[int]) -> list[int]: |
3 |
| - """ |
4 |
| - 1. For the higher bound, it is determined by either moving the leftmost |
5 |
| - to the right side, or by moving the rightmost to the left side: |
6 |
| - 1.1 If moving leftmost to the right side, the available moving |
7 |
| - positions are A[n - 1] - A[1] + 1 - (n - 1) = |
8 |
| - A[n - 1] - A[1] - n + 2 |
9 |
| - 1.2 If moving rightmost to the left side, the available moving |
10 |
| - positions are A[n - 2] - A[0] + 1 - (n - 1) = |
11 |
| - A[n - 2] - A[0] - n + 2. |
12 |
| - 2. For the lower bound, we could use sliding window to find a window |
13 |
| - that contains the most consecutive stones (A[i] - A[i - 1] = 1): |
14 |
| - 2.1 Generally the moves we need are the same as the number of |
15 |
| - missing stones in the current window. |
16 |
| - 2.3 When the window is already consecutive and contains all the |
17 |
| - n - 1 stones, we need at least 2 steps to move the last stone |
18 |
| - into the current window. For example, 1,2,3,4,10: |
19 |
| - 2.3.1 We need to move 1 to 6 first as we are not allowed to |
20 |
| - move 10 to 5 as it will still be an endpoint stone. |
21 |
| - 2.3.2 Then we need to move 10 to 5 and now the window becomes |
22 |
| - 2,3,4,5,6. |
23 |
| - """ |
24 |
| - A, N = sorted(stones), len(stones) |
25 |
| - maxMoves = max(A[N - 1] - A[1] - N + 2, A[N - 2] - A[0] - N + 2) |
26 |
| - minMoves = N |
| 4 | + ''' |
| 5 | + Test cases walk through |
| 6 | + Given 7, 4, 9 prove 1, 2 6, 5, 4, 3, 10, prove 2, 3 |
| 7 | +
|
| 8 | + Sort stones -> 4, 7, 9 3, 4, 5, 6, 10 |
| 9 | + Stone length -> 3 5 |
| 10 | + Move penultimate = 7 - 4 - 3 + 2 = 2 6-3-5+2 = 0 |
| 11 | + Move final = 9 - 7 - 3 + 2 = 1 10-4-5+2 = 3 |
| 12 | + Neither is 0, so we cannot return for sure Move penultimate is 0, so move final is assured |
| 13 | + This means we can return [min(2, 3), 3] -> [2, 3] |
| 14 | +
|
| 15 | + Max legal moves is 0 For completeness, max legal moves is 0, max moves is 3 |
| 16 | + starting index is 0 starting index is 0 |
27 | 17 |
|
28 |
| - # Calculate minimum moves through sliding window. |
29 |
| - start = 0 |
30 |
| - for end in range(N): |
31 |
| - while A[end] - A[start] + 1 > N: |
32 |
| - start += 1 |
| 18 | + Enumeration Enumeration |
| 19 | + index is 0, stone is 4 index is 0, stone is 3 |
| 20 | + stones[0] lte 4 - 3 ? No, skip while loop stones[0] lte 3 - 5 ? No, skip while |
| 21 | + max legal moves is min of (max of self and 0 - 0 + 1, most moves) max legal moves is min of (max of self and 0 - 0 + 1), max moves -> max legal moves is 1 |
| 22 | + -> max legal moves is 1 |
33 | 23 |
|
34 |
| - if end - start + 1 == N - 1 and A[end] - A[start] + 1 == N - 1: |
35 |
| - # Case: N - 1 stones with N - 1 positions. |
36 |
| - minMoves = min(minMoves, 2) |
37 |
| - else: |
38 |
| - minMoves = min(minMoves, N - (end - start + 1)) |
| 24 | + index is 1, stone is 7 index is 1, stone is 4 |
| 25 | + stones[0] <= 7 - 3 ? Yes, enter while stones[0] lte 4 - 5 ? No, skip while |
| 26 | + starting index is now 1 max legal moves is min of (max of self and 1 - 0 + 1), max moves -> max legal moves is 2 |
| 27 | + stones[1] <= 7 - 3 ? No, skip while |
| 28 | + max legal moves -> min(max of self and 1 - 1 + 1), max_moves |
| 29 | + -> max legal moves is 1 index is 2, stone is 5 |
| 30 | + stones[0] lte 5 - 5 ? No skip while |
| 31 | + index is 2, stone is 9 max legal moves is min of (max of self and 2 - 0 + 1), max_moves -> max legal moves is 3 |
| 32 | + stones[1] <= 9 - 3 ? No, skip while |
| 33 | + max legal moves is min(max of self and 2-1 + 1), max_moves |
| 34 | + -> max legal moves is 2 index is 3, stone is 6 |
| 35 | + End enumeration stones[0] lte 6 - 5 ? No skip while |
| 36 | + max legal moves is min (max of self and 3 - 0 + 1), max_moves -> max legal moves is 3 |
| 37 | + Return [3 - 2, 2] -> [1, 2] checks out |
| 38 | + index is 4, stones is 10 |
| 39 | + stones[0] lte 10 - 5 ? Yes, enter while |
| 40 | + starting index is 1 |
| 41 | + stones[1] lte 10 - 5 ? Yes, enter while |
| 42 | + starting index is 2 |
| 43 | + stones[2] lte 10 - 5 ? Yes, enter while |
| 44 | + starting index is 3 |
| 45 | + max legal moves is min (max of self and 4 - 3 + 1), max moves -> max legal moves is 3 |
| 46 | + End enumeration |
39 | 47 |
|
40 |
| - return [minMoves, maxMoves] |
| 48 | + Return [5 - 3, 3] -> [2, 3] |
| 49 | + ''' |
| 50 | + def numMovesStonesII(self, stones: List[int]) -> List[int] : |
| 51 | + # order does not need to be maintained, so sorting is optimal |
| 52 | + stones.sort() |
| 53 | + # want to work within stone physical space since 10^9 >> 10^4 (stone weight vs length) |
| 54 | + stone_length = len(stones) |
| 55 | + # what is the cost of moving the second to last stone and the 0th stone? |
| 56 | + move_penultimate = stones[-2] - stones[0] - stone_length + 2 |
| 57 | + # what is the cost of moving the last stone and the 1st stone? |
| 58 | + move_final = stones[-1] - stones[1] - stone_length + 2 |
| 59 | + # in both of these, the cost is the positional exchange in stones along the stone length + 2 for the two stones moving |
| 60 | + # our most moves possible are the max of these two |
| 61 | + most_moves = max(move_penultimate, move_final) |
| 62 | + # since the stones are unique, if either is 0, the one that we have must be max legal moves |
| 63 | + # if move penultimate is 0, that means that the second largest stone less the least stone less the length + 2 is 0 |
| 64 | + # this means that the largest stone, which must be at least one larger than the largest, less the second to least stone which is at least one larger than the least stone less the length + 2 is move final |
| 65 | + # our minimal length is 3 |
| 66 | + # let a, b, c be stones in order |
| 67 | + # b - a - 3 + 2 = 0 -> b = a + 1 move penultimate |
| 68 | + # c - b - 3 + 2 = 0 -> b = c - 1 move final |
| 69 | + # c - 1 = a + 1 -> c = a + 2 |
| 70 | + # all stones must be at least 1 to 10^9 and are unique |
| 71 | + # so at minimum a is 1, b is 2 and c is 3 |
| 72 | + # in this case, move final is also 0 so we get 0, 0 |
| 73 | + # if a = 4, b = 5, c = 7 |
| 74 | + # 5 - 4 - 3 + 2 = 0 move penultimate is 0 |
| 75 | + # 7 - 5 - 3 + 2 -> 1 move ultimate is 1 |
| 76 | + # min legal moves is min of 2 and 1 -> min legal moves is 1 -> 1, 1 is returned |
| 77 | + # from this it can be seen that the movement of c relative to b impacts the return here when one is 0, and that if either is 0 it does not preclude the other. However it does entail a relation to 2 as most that min could become |
| 78 | + # this is because if most moves is greater than 2, we could always do the move alternate that was 0 in two steps. This is what locks in to place the ability to use 2 here as the min argument. |
| 79 | + if move_penultimate == 0 or move_final == 0 : |
| 80 | + min_legal_moves = min(2, most_moves) |
| 81 | + return [min_legal_moves, most_moves] |
| 82 | + # how many legal moves are there in sorted order? |
| 83 | + max_legal_moves = 0 |
| 84 | + # starting from 0th index |
| 85 | + starting_index = 0 |
| 86 | + # enumerate each stone and index |
| 87 | + for index, stone in enumerate(stones) : |
| 88 | + # while the stone at starting index is lte this stone minus stone length (cost of a move) |
| 89 | + while stones[starting_index] <= stone - stone_length : |
| 90 | + # increment |
| 91 | + starting_index += 1 |
| 92 | + # max legal moves is then set to maxima of self and indexed difference with 1 for 0 based indexing |
| 93 | + max_legal_moves = min(max(max_legal_moves, index - starting_index + 1), most_moves) |
| 94 | + # return length - max legal moves when in sorted order (your minimal move state) and most moves in sorted order |
| 95 | + return [stone_length - max_legal_moves, most_moves] |
0 commit comments