Champagne Tower
MedPrompt
We stack glasses in a pyramid, where the first row has 1 glass, the second row has 2 glasses, and so on until the 100th row. Each glass holds one cup of champagne.
Then, some champagne is poured into the first glass at the top. When the topmost glass is full, any excess liquid poured will fall equally to the glass immediately to the left and right of it. When those glasses become full, any excess champagne will fall equally to the left and right of those glasses, and so on. (A glass at the bottom row has its excess champagne fall on the floor.)
For example, after one cup of champagne is poured, the top most glass is full. After two cups of champagne are poured, the two glasses on the second row are half full. After three cups of champagne are poured, those two cups become full - there are 3 full glasses total now. After four cups of champagne are poured, the third row has the middle glass half full, and the two outside glasses are a quarter full, as pictured below.

Now after pouring some non-negative integer cups of champagne, return how full the jth glass in the ith row is (both i and j are 0-indexed.)
Example 1:
Input: poured = 1, query_row = 1, query_glass = 1
Output: 0.00000
Explanation: We poured 1 cup of champange to the top glass of the tower (which is indexed as (0, 0)). There will be no excess liquid so all the glasses under the top glass will remain empty.Example 2:
Input: poured = 2, query_row = 1, query_glass = 1
Output: 0.50000
Explanation: We poured 2 cups of champange to the top glass of the tower (which is indexed as (0, 0)). There is one cup of excess liquid. The glass indexed as (1, 0) and the glass indexed as (1, 1) will share the excess liquid equally, and each will get half cup of champange.Example 3:
Input: poured = 100000009, query_row = 33, query_glass = 17
Output: 1.00000
Constraints:
0 <= poured <= 1090 <= query_glass <= query_row < 100
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the flow of champagne through the pyramid of glasses row by row. We use a 2D array to store the amount of champagne that flows into each glass, which directly models the structure of the pyramid.
Algorithm
- Create a 2D array
towerofdoubles with dimensions(query_row + 2) x (query_row + 2)to store the amount of champagne flowing into each glass. Initialize all elements to 0. - Set the top glass
tower[0][0]to the total amountpoured. - Iterate from row
r = 0toquery_row. - Inside this loop, iterate through each glass
cin the current rowr(fromc = 0tor). - Check if the champagne in the current glass
tower[r][c]exceeds its capacity of 1.0. - If it does, calculate the
excess = tower[r][c] - 1.0. - Distribute this
excessequally to the two glasses directly below it:tower[r+1][c]andtower[r+1][c+1]each receiveexcess / 2.0. - After the loops complete, the value
tower[query_row][query_glass]holds the total champagne that has flowed into the target glass. - The final answer is the amount in the glass, which cannot exceed 1.0. So, return
min(1.0, tower[query_row][query_glass]).
Walkthrough
We can model the pyramid as a 2D array, let's call it tower, where tower[i][j] represents the total amount of champagne that flows into the glass at row i, column j.
The size of this array needs to be at least (query_row + 1) x (query_row + 1). A 102x102 array is sufficient given the constraints.
We start by pouring all the champagne, poured, into the top glass tower[0][0].
Then, we iterate from the top row (r = 0) down to the query_row. For each glass (r, c) in the current row, we check if it has more champagne than its capacity (which is 1 cup).
If tower[r][c] > 1, the excess champagne (tower[r][c] - 1) is split equally and flows down to the two glasses below it: (r+1, c) and (r+1, c+1). We update their values in the tower array accordingly by adding excess / 2.0 to each.
After iterating through all necessary rows, the value tower[query_row][query_glass] will hold the total amount of champagne that has flowed into the target glass.
The amount of champagne actually held by the glass is this value, capped at 1.0. So, the final answer is min(1.0, tower[query_row][query_glass]).
class Solution { public double champagneTower(int poured, int query_row, int query_glass) { double[][] tower = new double[102][102]; tower[0][0] = (double) poured; for (int r = 0; r <= query_row; r++) { for (int c = 0; c <= r; c++) { if (tower[r][c] > 1.0) { double excess = tower[r][c] - 1.0; tower[r+1][c] += excess / 2.0; tower[r+1][c+1] += excess / 2.0; } } } return Math.min(1.0, tower[query_row][query_glass]); }}Complexity
Time
O(R^2), where R is `query_row`. We have two nested loops, each running up to R times.
Space
O(R^2), where R is `query_row`. We use a 2D array of size approximately `(R+1) x (R+1)`.
Trade-offs
Pros
Simple to understand and implement as it directly models the physical process.
Sufficiently efficient for the given constraints.
Cons
Uses more memory than necessary. The space complexity is quadratic with respect to the query row, which can be optimized.
This approach is an optimization of the 2D array simulation. We observe that to calculate the champagne flow for any given row, we only need the information from the immediately preceding row. This allows us to reduce the space complexity from O(R^2) to O(R), where R is the query_row, by using a single 1D array.
Algorithm
- Create a 1D array
dpofdoubles with sizequery_row + 2. - Initialize
dp[0] = poured, representing the initial state at the top glass. - Iterate from row
i = 0toquery_row - 1. This loop processes rowito calculate the flows for the next row,i+1. - Inside this loop, iterate through the glasses of row
ifrom right to left (fromj = idown to0). - For each glass
j, calculate the total excess flow:excess = Math.max(0.0, dp[j] - 1.0). - The flow to the right child
(i+1, j+1)isexcess / 2.0. Add this amount todp[j+1]. - The flow to the left child
(i+1, j)is alsoexcess / 2.0. Updatedp[j]to become this value. The right-to-left traversal ensures this update doesn't interfere with the calculation for other glasses in the same row. - After the loops complete,
dpholds the flow values for thequery_row. - Return
min(1.0, dp[query_glass]).
Walkthrough
Instead of a 2D array, we use a single 1D array, dp, of size query_row + 2 to represent the flow of champagne. dp[j] will store the total amount of champagne flowing into glass j of the current row being processed.
We initialize dp[0] with the total poured amount, as all champagne starts at the top glass.
We then iterate from row i = 0 up to query_row - 1. In each iteration i, we calculate the champagne distribution for the next row, i+1, based on the overflows from row i.
To update the dp array in-place, we must iterate through the glasses of the current row i from right to left (from j = i down to 0). This is crucial because the new flow for dp[j] depends on the old flows from dp[j] and dp[j-1]. A right-to-left traversal ensures we use the old dp[j] value to update dp[j+1] before dp[j] itself is updated.
For each glass (i, j), we calculate the excess flow, which is max(0, dp[j] - 1.0). This excess is split in half, with excess / 2.0 going to the left child (i+1, j) and excess / 2.0 going to the right child (i+1, j+1).
In our 1D array, this translates to: we set dp[j] to excess / 2.0 and add excess / 2.0 to dp[j+1]. The right-to-left update correctly accumulates the flows from both parents for the next row.
After the loops complete, dp[query_glass] contains the total flow into the target glass (query_row, query_glass).
The final answer is min(1.0, dp[query_glass]).
class Solution { public double champagneTower(int poured, int query_row, int query_glass) { double[] dp = new double[query_row + 2]; dp[0] = (double) poured; for (int i = 0; i < query_row; i++) { for (int j = i; j >= 0; j--) { double excess = Math.max(0.0, dp[j] - 1.0); dp[j+1] += excess / 2.0; dp[j] = excess / 2.0; } } return Math.min(1.0, dp[query_glass]); }}Complexity
Time
O(R^2), where R is `query_row`. The nested loops dominate the runtime.
Space
O(R), where R is `query_row`. We use a single 1D array of size `R+2`.
Trade-offs
Pros
Highly efficient in terms of space, using only linear extra space.
Maintains the same optimal time complexity as the 2D approach.
Cons
The in-place update logic with the right-to-left loop can be slightly less intuitive to grasp compared to the 2D array version.
Solutions
Solution
class Solution {public double champagneTower(int poured, int query_row, int query_glass) { double[][] f = new double[101][101]; f[0][0] = poured; for (int i = 0; i <= query_row; ++i) { for (int j = 0; j <= i; ++j) { if (f[i][j] > 1) { double half = (f[i][j] - 1) / 2.0; f[i][j] = 1; f[i + 1][j] += half; f[i + 1][j + 1] += half; } } } return f[query_row][query_glass]; }}class Solution {public: double champagneTower(int poured, int query_row, int query_glass) { double f[101][101] = {0.0}; f[0][0] = poured; for (int i = 0; i <= query_row; ++i) { for (int j = 0; j <= i; ++j) { if (f[i][j] > 1) { double half = (f[i][j] - 1) / 2.0; f[i][j] = 1; f[i + 1][j] += half; f[i + 1][j + 1] += half; } } } return f[query_row][query_glass]; }};class Solution: def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: f = [[0] * 101 for _ in range(101)] f[0][0] = poured for i in range(query_row + 1): for j in range(i + 1): if f[i][j] > 1: half = (f[i][j] - 1) / 2 f[i][j] = 1 f[i + 1][j] += half f[i + 1][j + 1] += half return f[query_row][query_glass]Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.