|
2 | 2 |
|
3 | 3 | ## Initial thoughts (stream-of-consciousness)
|
4 | 4 |
|
| 5 | +- all robots are moving at the same speed, so any moving in same direction can't collide |
| 6 | + - if all characters in `directions` are the same, can just return `healths` |
| 7 | +- also, any moving away from each other won't collide |
| 8 | + - after filtering for same direction, can filter remaining opposite-direction movers for instances where one moving `R` has a larger `directions` value than another moving `L`. |
| 9 | +- this feels complex enough that I think it might be worth writing a `Robot` class to track attributes for each robot |
| 10 | +- order in which collisions happens will be important, because removed robot then won't collide with subsequent ones it would've otherwise encountered |
| 11 | + - maybe this means I'll want to simulate this in terms "timesteps" so I can make sure to execute collision logic in the right order |
| 12 | + - though simulation solutions are rarely the most optimal, and it seems like leetcode usually includes a test case that'd cause simulation-based solutions to time out |
| 13 | + - I think the scenario where a simulation would be bad is if the colloding robots start very far from each other, causing me to have to simulate a bunch of noop timesteps (and check whether any robots are in the same location for each of them) before something actually happens. But I think I can avoid this by -- at each timestep where a collision happens, as well as the first -- finding the minimum distance between collision-eligible pair of robots and immediately advancing time by that many steps. |
| 14 | +- instead of simulating individual timesteps, could I figure this out from the initial positions and direction of the robots? |
| 15 | + - I think so, if I start with the rightmost robot moving right and collide it with further-right robots that're moving left until it's dead... at which point I could move onto the next-rightmost right-moving robot and collide it with further-right left-moving robots |
| 16 | + - Would this work with a scenario like example 3, where there are two indepenedent, simultaneous collisions? |
| 17 | + - collide robot 3 with robot 4 |
| 18 | + - if it survives, we know it'll be in the returned array and we can move onto robot 1 |
| 19 | + - if it dies, we also move onto robot 1 |
| 20 | + - in either case, next move onto robot 1, collide it with robot 2 |
| 21 | + - if it survives |
| 22 | + - if robot 4 survived the robot 3 vs 4 collision |
| 23 | + - collide robot 1 with robot 4 |
| 24 | + - the survivor will be in the returned array |
| 25 | + - if robot 3 survived the robot 3 vs 4 collision |
| 26 | + - robot 1 will be in the returned array |
| 27 | + - if it dies, robot 2 *and* robot 4 will be in the returned array |
| 28 | + - I *think* this will work... I think I'll just need 2 separate lists(?) to keep track of right-moving and left-moving robots independently, so I can figure out which to collide with which |
| 29 | + - also, both will need to be sorted so I can access the "next" robot from each list correctly |
| 30 | + - I'd need to loop over robots to construct those two list anyway... if I pre-sort the initial list of robots moving in both directions, can I just do the collisions at the same time? |
| 31 | + - this feels like it's trying to get me to use a stack... or two? |
| 32 | + - let's say I get the list of robots in position order (I'll figure out how later). Then I could: |
| 33 | + - create a stack for right-moving robots (and one for left-moving robots?) |
| 34 | + - for each robot |
| 35 | + - if it's moving right, push it onto a stack of right-moving robots |
| 36 | + - this means the stack will also be in order of position, with the rightmost right-moving robot on top... yeah this definitely feels like how they're trying to get me to solve it... I think |
| 37 | + - else (it's moving left), check whether there are any right-moving robots on the stack |
| 38 | + - if there aren't, we know that robot is a survivor, and can move onto the next robot in the list |
| 39 | + - else (there are) collide it with the right-moving robot on top of the stack |
| 40 | + - if the left-moving robot survives, pop the right-moving robot off the stack (can discard because we know it didn't survive) and collide the left-moving robot with the new top-most right-moving robot, and so on |
| 41 | + - (I think this means I'll actually need to use a `while` loop for checking whether there are robots in the stack instead of `if`/`else`) |
| 42 | + - else (the right-moving robot survived) move onto the next robot in the sorted list and repeat |
| 43 | + - ah, also need to account for equal health -- neither survives in that case |
| 44 | + - any robots on the stack when we get to the end of the list are survivors |
| 45 | + - also, seems like I *won't* need a separate stack for left-moving robots |
| 46 | +- how do i get the survivor list back into the right order? |
| 47 | + - if I create a bunch of `Robot` objects, I could initialize them with an attr for their original index, and then sort the survivor list by `key=lambda robot: robot.<that_attr>` |
| 48 | + - but the constraints say there can be up to $10^5$ robots, so creating that many objects -- even small, optimized ones with `namedtuple` or `dataclass` -- would take a ton of memory and also initialization time... |
| 49 | + - what value do I keep track of in the stack of right-moving robots if I'm not using `Robot` objects though? |
| 50 | + - could I create some sort of mapping between the robots' pre- and post-sorting indices? |
| 51 | + - `np.argsort` would be nice here... alas |
| 52 | + - could I create the equivalent output some other way? |
| 53 | + - I could create a list of indices, and then sort that according to `positions` without *actually* sorting positions... then instead of sorting `positions` in the first place and mapping thos indices onto `healths`, `directions` , then trying to map backwards, I could use the sorted list of indices to get the next index from all 3 input lists for the current iteration. |
| 54 | + - ooo important note with this though -- I wouldn't want to actually remove any defeated robots from any of the lists while iterating, because that would mess up the mapping. Also, once I determine the "current" robot is a survivor, I can't just append them to the list I'll eventually return because I'm iterating in sorted order and not input order. |
| 55 | + - *But* all the input lists are still in their original order, including `healths`, which is the basis for the values I need to return... so I could just modify `healths` in-place and return that at the end. |
| 56 | + - I'd have to filter 0's to remove defeated robots before returning it... which ultimately means another loop, so this might just be a wash. But it's the idea I have right now so I'll go with it... |
| 57 | + |
5 | 58 | ## Refining the problem, round 2 thoughts
|
6 | 59 |
|
7 | 60 | ## Attempted solution(s)
|
| 61 | + |
8 | 62 | ```python
|
9 |
| -class Solution: # paste your code here! |
10 |
| - ... |
| 63 | +class Solution: |
| 64 | + def survivedRobotsHealths(self, positions: List[int], healths: List[int], directions: str) -> List[int]: |
| 65 | + sorted_ixs = list(range(len(positions))) |
| 66 | + sorted_ixs.sort(key=lambda i: positions[i]) |
| 67 | + right_movers_stack = [] |
| 68 | + |
| 69 | + for curr_ix in sorted_ixs: |
| 70 | + if directions[curr_ix] == 'R': |
| 71 | + right_movers_stack.append(curr_ix) |
| 72 | + else: |
| 73 | + while len(right_movers_stack) > 0: |
| 74 | + rightmost_right_mover_ix = right_movers_stack[-1] |
| 75 | + rightmost_right_mover_health = healths[rightmost_right_mover_ix] |
| 76 | + if rightmost_right_mover_health > healths[curr_ix]: |
| 77 | + healths[curr_ix] = 0 |
| 78 | + healths[rightmost_right_mover_ix] -= 1 |
| 79 | + break |
| 80 | + elif rightmost_right_mover_health == healths[curr_ix]: |
| 81 | + healths[curr_ix] = 0 |
| 82 | + healths[rightmost_right_mover_ix] = 0 |
| 83 | + right_movers_stack.pop() |
| 84 | + break |
| 85 | + else: |
| 86 | + right_movers_stack.pop() |
| 87 | + healths[rightmost_right_mover_ix] = 0 |
| 88 | + healths[curr_ix] -= 1 |
| 89 | + |
| 90 | + return list(filter(None, healths)) |
11 | 91 | ```
|
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +yay! |
0 commit comments