799. Champagne Tower - Explanation

Problem Link



1. Recursion

class Solution:
    def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float:
        def rec(row, glass):
            if row < 0 or glass < 0 or glass > row:
                return 0

            if row == 0 and glass == 0:
                return poured

            left_parent = max(0, rec(row - 1, glass - 1) - 1)
            right_parent = max(0, rec(row - 1, glass) - 1)

            return (left_parent + right_parent) / 2

        return min(1, rec(query_row, query_glass))

Time & Space Complexity

  • Time complexity: O(2n)O(2 ^ n)
  • Space complexity: O(n)O(n)

Where nn is the given queryRowqueryRow.


2. Dynamic Programming (Top-Down)

class Solution:
    def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float:
        memo = { (0, 0) : poured }

        def rec(row, glass):
            if row < 0 or glass < 0 or glass > row:
                return 0
            if (row, glass) in memo:
                return memo[(row, glass)]

            left_parent = max(0, rec(row - 1, glass - 1) - 1)
            right_parent = max(0, rec(row - 1, glass) - 1)

            memo[(row, glass)] = (left_parent + right_parent) / 2
            return memo[(row, glass)]

        return min(1, rec(query_row, query_glass))

Time & Space Complexity

  • Time complexity: O(nm)O(n * m)
  • Space complexity: O(nm)O(n * m)

Where nn is the given queryRowqueryRow and mm is the given queryGlassqueryGlass.


3. Dynamic Programming (Bottom-Up)

class Solution:
    def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float:
        dp = [[0] * (i + 1) for i in range(query_row + 5)]
        dp[0][0] += poured

        for row in range(min(99, query_row + 1)):
            for glass in range(row + 1):
                excess = (dp[row][glass] - 1.0) / 2.0
                if excess > 0:
                    dp[row + 1][glass] += excess
                    dp[row + 1][glass + 1] += excess

        return min(1.0, dp[query_row][query_glass])

Time & Space Complexity

  • Time complexity: O(nm)O(n * m)
  • Space complexity: O(nm)O(n * m)

Where nn is the given queryRowqueryRow and mm is the given queryGlassqueryGlass.


4. Dynamic Programming (Space Optimized)

class Solution:
    def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float:
        prev_row = [poured]  # Flow

        for row in range(1, query_row + 1):
            cur_row = [0] * (row + 1)
            for i in range(row):
                extra = prev_row[i] - 1
                if extra > 0:
                    cur_row[i] += 0.5 * extra
                    cur_row[i + 1] += 0.5 * extra
            prev_row = cur_row

        return min(1, prev_row[query_glass])

Time & Space Complexity

  • Time complexity: O(nm)O(n * m)
  • Space complexity: O(n)O(n)

Where nn is the given queryRowqueryRow and mm is the given queryGlassqueryGlass.


5. Dynamic Programming (Optimal)

class Solution:
    def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float:
        dp = [poured] + [0] * query_row

        for row in range(1, query_row + 1):
            for i in range(row - 1, -1, -1):
                extra = dp[i] - 1
                if extra > 0:
                    dp[i] = 0.5 * extra
                    dp[i + 1] += 0.5 * extra
                else:
                    dp[i] = 0

        return min(1, dp[query_glass])

Time & Space Complexity

  • Time complexity: O(nm)O(n * m)
  • Space complexity: O(n)O(n)

Where nn is the given queryRowqueryRow and mm is the given queryGlassqueryGlass.