def get_lower_bound(floors, loc):
    """
    Computes a lower bound for the number of moves required to move all chips
    and generators to the top.

    The optimal strategy is to move to the bottom layer (taking one item to
    power the elevator), then move all items up to the next floor (move two
    items up then one item down until two remain then move both up together).

    This process repeats until everything is on the top floor. This lower bound
    doesn't take into account other compatibility conditions such as valid
    combinations of items in elevators / on floors.
    """

    def add_min_moves(count):
        # number of moves required to move `count` items up to the next floor
        if count <= 2:
            # if 2 items or fewer, can do it in a single move
            return 1
        # otherwise we require two moves per item (take two up, move one back
        # down) except the last two which require only one move
        return 2 * (count - 2) + 1

    # initialise lower bound
    lower_bound = 0

    # count the number of items on each floor
    counts = [len(floor) for floor in floors]

    # find out which is the lowest floor with items
    first_non_empty = [count > 0 for count in counts].index(True)

    if loc < first_non_empty:
        # if we start below the first floor with items, we don't have anything
        # to power the elevator!
        raise ValueError(
            "You need an item on the current floor to power the elevator"
        )
    elif loc > first_non_empty:
        # we move down to the lowest non-empty floor, taking one item with us
        lower_bound += loc - first_non_empty
        counts[loc] -= 1
        counts[first_non_empty] += 1

    cumulative_count = 0
    for count in counts[:-1]:
        # for each floor except the last, we move all items to the next floor
        cumulative_count += count
        lower_bound += add_min_moves(cumulative_count)

    return lower_bound