pythonalgorithmdata-structurestime-complexitygreedy

How can I enhance the time complexity of arranging bricks into stacks based on specific conditions?


I’m working on a problem where I need to arrange N bricks of varying lengths into the least number of stacks. The rules for stacking bricks are that a brick 𝑖 can be placed on top of brick 𝑗 only if the condition 𝐴𝑖 + 𝑥 ≤ 𝐴𝑗 holds true, where 𝑥 is a given integer.

Here is my current implementation in Python:

def arrange_bricks(N, x, bricks):
    # Sort the bricks in descending order
    bricks.sort(reverse=True)

    # List to hold stacks
    stacks = []

    for brick in bricks:
        placed = False
        for stack in stacks:
            if brick + x <= stack[-1]:
                stack.append(brick)
                placed = True
                break
        
        if not placed:
            stacks.append([brick])

    print(len(stacks))
    for stack in stacks:
        print(len(stack), ' '.join(map(str, stack)))

N, x = map(int, input().split())
bricks = list(map(int, input().split()))
arrange_bricks(N, x, bricks)

Problem: Time Complexity: Currently, the time complexity of my solution is 𝑂(𝑁^2) in the worst case, as each brick potentially checks against all existing stacks. This can be inefficient, especially when 𝑁 is large (up to 10^6).

Desired Outcome: I want to enhance the time complexity to be more efficient, preferably around 𝑂(𝑁log𝑁)if possible.

Constraints:

• 1 ≤ 𝑁 ≤ 10^6

• 1 ≤ 𝑥 ≤ 10^9

• 1 ≤ 𝐴𝑖 ≤ 10^9

Questions: How can I modify my approach to use a more efficient data structure or algorithm to reduce the time complexity? Are there specific algorithms or techniques I can apply to solve this problem more effectively? Thank you for your help!


Solution

  • You can optimize your solution to O(N) after the initial sorting of the bricks (so it's O(N log N) overall).

    Instead of trying the current brick on all stacks, try just the one with the longest top brick. Keep the stacks sorted by top brick, from longest top brick to shortest top brick. This way you just try the first stack. If you do place the current brick onto it, then that stack now has the shortest top brick, so move that stack to the end.

    So import deque from collections and change your

        # List to hold stacks
        stacks = []
    
        for brick in bricks:
            placed = False
            for stack in stacks:
                if brick + x <= stack[-1]:
                    stack.append(brick)
                    placed = True
                    break
            
            if not placed:
                stacks.append([brick])
    

    to this:

        # Deque to hold stacks
        stacks = deque()
    
        for brick in bricks:
            if stacks and brick + x <= stacks[0][-1]:
                stacks[0].append(brick)
                stacks.rotate(-1)
            else:
                stacks.append([brick])
    

    Attempt This Online!