PracHub
QuestionsPremiumLearningGuidesInterview PrepNEWCoaches

Quick Overview

This question evaluates understanding of concurrent resource management, advanced data structures for top-K and per-user queries, enforcement of caps and expirations, and algorithmic complexity guarantees while maintaining atomicity under concurrent operations.

  • Medium
  • OpenAI
  • Coding & Algorithms
  • Software Engineer

Implement a GPU credit manager

Company: OpenAI

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: Medium

Interview Round: Technical Screen

Implement a GPU credit manager for a compute cluster. Each user has a nonnegative credit balance that can be increased (grantCredits(user, amount)), consumed when scheduling jobs (consume(user, amount) -> boolean), and refunded when jobs finish (refund(user, amount)). Support querying a user’s remaining credits (get(user) -> int) and retrieving the top K users by balance (topK(k) -> list). Enforce per-user maximum caps, optional per-organization aggregate caps, and optional credit expirations. Prevent balances from going negative and ensure atomicity under concurrent calls. Target O(log n) time per operation and near-linear space. Describe the data structures, provide pseudocode for each API, and analyze time and space complexity.

Quick Answer: This question evaluates understanding of concurrent resource management, advanced data structures for top-K and per-user queries, enforcement of caps and expirations, and algorithmic complexity guarantees while maintaining atomicity under concurrent operations.

Process a sequence of timestamped operations for a single-threaded GPU credit manager. Rules: - Every user starts with 0 credits. - `user_caps[user]` is that user's maximum balance. If a user is missing from `user_caps`, their cap is unlimited. - `user_org[user]` gives the user's organization. If a user is missing from `user_org`, they have no organization. - `org_caps[org]` is the maximum total active balance across all users in that organization. If an organization is missing from `org_caps`, its cap is unlimited. - Operations are given in nondecreasing timestamp order and are processed atomically. - Credits are stored in lots. A granted lot may expire at a specific timestamp. Before processing any operation at time `t`, all remaining credits from lots with `expire_time <= t` disappear. - When consuming credits, always consume from the earliest-expiring active lots first. Non-expiring lots are treated as expiring last. - `grant` and `refund` are partially applied if necessary: add as many credits as possible without violating the user cap or the organization cap. - Balances can never go negative. - `topK(k)` returns up to `k` users with positive balances, sorted by balance descending, then by user name ascending. Implement `solution(user_caps, user_org, org_caps, operations)` and return a list containing the result of every operation in order. Operation formats: - `('grant', t, user, amount, expire_time)` -> return the integer amount actually added. `expire_time` is an integer timestamp or `None`. - `('refund', t, user, amount)` -> return the integer amount actually added. Refunded credits never expire. - `('consume', t, user, amount)` -> return `True` if the full amount was consumed, otherwise `False`. - `('get', t, user)` -> return the user's current active balance. - `('topK', t, k)` -> return a list of `[user, balance]` pairs.

Constraints

  • 0 <= len(operations) <= 2 * 10^5
  • Operation timestamps are in nondecreasing order
  • 0 <= amount, cap <= 10^9
  • User and organization names are non-empty strings
  • If `expire_time` is not `None`, it is an integer timestamp; a lot with `expire_time <= t` is considered expired before time `t` operations run

Examples

Input: ({}, {}, {}, [])

Expected Output: []

Explanation: With no operations, there are no results to return.

Input: ({'alice': 10, 'bob': 7}, {'alice': 'org1', 'bob': 'org1', 'carol': 'org2'}, {'org1': 12, 'org2': 20}, [('grant', 1, 'alice', 8, 5), ('grant', 2, 'bob', 7, None), ('consume', 3, 'alice', 3), ('refund', 4, 'bob', 5), ('get', 4, 'bob'), ('topK', 4, 2), ('get', 5, 'alice'), ('topK', 6, 3)])

Expected Output: [8, 4, True, 3, 7, [['bob', 7], ['alice', 5]], 0, [['bob', 7]]]

Explanation: Bob's first grant is limited by the organization cap, Bob's refund is also capped, and Alice's remaining expiring credits disappear at time 5.

Input: ({'u': 20}, {}, {}, [('grant', 1, 'u', 5, 10), ('grant', 2, 'u', 7, 6), ('consume', 3, 'u', 6), ('get', 5, 'u'), ('get', 6, 'u'), ('topK', 6, 1)])

Expected Output: [5, 7, True, 6, 5, [['u', 5]]]

Explanation: Consumption uses the earliest-expiring lot first, leaving 1 credit in the lot expiring at time 6, which then expires before the `get` at time 6.

Input: ({'a': 10, 'b': 10, 'c': 10}, {'a': 'o', 'b': 'o', 'c': 'o2'}, {'o': 9, 'o2': 10}, [('grant', 1, 'a', 5, None), ('grant', 1, 'b', 5, None), ('grant', 1, 'c', 5, None), ('topK', 1, 3), ('consume', 2, 'a', 2), ('refund', 3, 'b', 10), ('topK', 3, 3)])

Expected Output: [5, 4, 5, [['a', 5], ['c', 5], ['b', 4]], True, 2, [['b', 6], ['c', 5], ['a', 3]]]

Explanation: User `b` is limited by the organization cap both on the initial grant and on the later refund. In the first `topK`, `a` and `c` tie at 5, so `a` comes first lexicographically.

Input: ({'x': 5}, {}, {}, [('topK', 0, 2), ('consume', 1, 'x', 1), ('refund', 2, 'x', 4), ('grant', 3, 'x', 3, None), ('consume', 4, 'x', 5), ('get', 5, 'x')])

Expected Output: [[], False, 4, 1, True, 0]

Explanation: This covers an empty `topK`, a failed consume on a zero balance, and a grant that is partially applied because user `x` is already near the cap.

Solution

def solution(user_caps, user_org, org_caps, operations):
    import heapq
    from collections import defaultdict

    INF = 10 ** 30

    user_balance = defaultdict(int)
    org_balance = defaultdict(int)
    user_lots = defaultdict(list)      # user -> min-heap of (expire_time, lot_id)
    expire_heap = []                   # global min-heap of (expire_time, lot_id)
    top_heap = []                      # max-heap simulated with (-balance, user)
    lot_info = {}                      # lot_id -> [remaining_amount, user, org, expire_time]
    next_lot_id = 0

    def push_top(user):
        bal = user_balance[user]
        if bal > 0:
            heapq.heappush(top_heap, (-bal, user))

    def process_expired(t):
        while expire_heap and expire_heap[0][0] <= t:
            _, lot_id = heapq.heappop(expire_heap)
            info = lot_info.pop(lot_id, None)
            if info is None:
                continue
            remaining, user, org, _ = info
            if remaining <= 0:
                continue
            user_balance[user] -= remaining
            if org is not None:
                org_balance[org] -= remaining
            push_top(user)

    def add_credits(user, amount, expire_time, current_time):
        nonlocal next_lot_id

        if amount <= 0:
            return 0
        if expire_time is not None and expire_time <= current_time:
            return 0

        org = user_org.get(user)
        user_cap = user_caps.get(user, INF)
        org_cap = INF if org is None else org_caps.get(org, INF)

        user_room = user_cap - user_balance[user]
        org_room = org_cap - (org_balance[org] if org is not None else 0)
        actual = min(amount, user_room, org_room)

        if actual <= 0:
            return 0

        exp = INF if expire_time is None else expire_time
        lot_id = next_lot_id
        next_lot_id += 1

        lot_info[lot_id] = [actual, user, org, exp]
        heapq.heappush(user_lots[user], (exp, lot_id))
        if exp != INF:
            heapq.heappush(expire_heap, (exp, lot_id))

        user_balance[user] += actual
        if org is not None:
            org_balance[org] += actual
        push_top(user)
        return actual

    def consume(user, amount):
        if amount < 0:
            return False
        if user_balance[user] < amount:
            return False
        if amount == 0:
            return True

        remaining_to_consume = amount
        heap = user_lots[user]

        while remaining_to_consume > 0:
            while heap:
                exp, lot_id = heapq.heappop(heap)
                info = lot_info.get(lot_id)
                if info is None:
                    continue

                remaining, lot_user, org, _ = info
                take = min(remaining, remaining_to_consume)
                new_remaining = remaining - take
                remaining_to_consume -= take

                if new_remaining == 0:
                    del lot_info[lot_id]
                else:
                    info[0] = new_remaining
                    heapq.heappush(heap, (exp, lot_id))
                break
            else:
                return False

        org = user_org.get(user)
        user_balance[user] -= amount
        if org is not None:
            org_balance[org] -= amount
        push_top(user)
        return True

    def top_k(k):
        if k <= 0:
            return []

        result = []
        popped_valid = []
        seen = set()

        while top_heap and len(result) < k:
            neg_bal, user = heapq.heappop(top_heap)
            bal = -neg_bal
            current = user_balance[user]

            if current <= 0 or bal != current or user in seen:
                continue

            result.append([user, current])
            popped_valid.append((neg_bal, user))
            seen.add(user)

        for item in popped_valid:
            heapq.heappush(top_heap, item)

        return result

    results = []

    for op in operations:
        kind = op[0]
        t = op[1]
        process_expired(t)

        if kind == 'grant':
            _, _, user, amount, expire_time = op
            results.append(add_credits(user, amount, expire_time, t))
        elif kind == 'refund':
            _, _, user, amount = op
            results.append(add_credits(user, amount, None, t))
        elif kind == 'consume':
            _, _, user, amount = op
            results.append(consume(user, amount))
        elif kind == 'get':
            _, _, user = op
            results.append(user_balance[user])
        elif kind == 'topK':
            _, _, k = op
            results.append(top_k(k))
        else:
            raise ValueError('Unknown operation: ' + str(kind))

    return results

Time complexity: Amortized O((1 + r) log n) per operation, where r is the number of credit lots consumed or expired during that call; `topK(k)` is O(k log n).. Space complexity: O(U + L + Q) worst-case with lazy heap entries, where U is the number of users, L is the number of live lots, and Q is the number of operations; this is near-linear in the input size..

Hints

  1. You need two different orderings at once: earliest expiration for consuming/expiring lots, and largest current balance for `topK`. Heaps plus hash maps are a strong fit.
  2. For `topK`, avoid updating heap entries in place. Push new balance entries and skip stale ones later with lazy deletion.
Last updated: Apr 30, 2026

Loading coding console...

PracHub

Master your tech interviews with 7,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Implement IP Address Arithmetic - OpenAI (hard)
  • Simulate Infection Spread on a Grid - OpenAI (hard)
  • Implement Social Follow Recommendations - OpenAI (medium)
  • Build a Compose Rating Card - OpenAI (medium)
  • Generate Data Labeling Schedules - OpenAI (medium)