|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[799. 香槟塔](https://leetcode.cn/problems/champagne-tower/solution/by-ac_oier-c8jn/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「动态规划」、「线性 DP」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +我们把玻璃杯摆成金字塔的形状,其中 第一层 有 `1` 个玻璃杯, 第二层 有 `2` 个,依次类推到第 `100` 层,每个玻璃杯 (`250ml`) 将盛有香槟。 |
| 10 | + |
| 11 | +从顶层的第一个玻璃杯开始倾倒一些香槟,当顶层的杯子满了,任何溢出的香槟都会立刻等流量的流向左右两侧的玻璃杯。当左右两边的杯子也满了,就会等流量的流向它们左右两边的杯子,依次类推。(当最底层的玻璃杯满了,香槟会流到地板上) |
| 12 | + |
| 13 | +例如,在倾倒一杯香槟后,最顶层的玻璃杯满了。倾倒了两杯香槟后,第二层的两个玻璃杯各自盛放一半的香槟。在倒三杯香槟后,第二层的香槟满了 - 此时总共有三个满的玻璃杯。在倒第四杯后,第三层中间的玻璃杯盛放了一半的香槟,他两边的玻璃杯各自盛放了四分之一的香槟,如下图所示。 |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +现在当倾倒了非负整数杯香槟后,返回第 i 行 j 个玻璃杯所盛放的香槟占玻璃杯容积的比例( i 和 j 都从0开始)。 |
| 18 | + |
| 19 | +示例 1: |
| 20 | +``` |
| 21 | +输入: poured(倾倒香槟总杯数) = 1, query_glass(杯子的位置数) = 1, query_row(行数) = 1 |
| 22 | +
|
| 23 | +输出: 0.00000 |
| 24 | +
|
| 25 | +解释: 我们在顶层(下标是(0,0))倒了一杯香槟后,没有溢出,因此所有在顶层以下的玻璃杯都是空的。 |
| 26 | +``` |
| 27 | +示例 2: |
| 28 | +``` |
| 29 | +输入: poured(倾倒香槟总杯数) = 2, query_glass(杯子的位置数) = 1, query_row(行数) = 1 |
| 30 | +
|
| 31 | +输出: 0.50000 |
| 32 | +
|
| 33 | +解释: 我们在顶层(下标是(0,0)倒了两杯香槟后,有一杯量的香槟将从顶层溢出,位于(1,0)的玻璃杯和(1,1)的玻璃杯平分了这一杯香槟,所以每个玻璃杯有一半的香槟。 |
| 34 | +``` |
| 35 | +示例 3: |
| 36 | +``` |
| 37 | +输入: poured = 100000009, query_row = 33, query_glass = 17 |
| 38 | +
|
| 39 | +输出: 1.00000 |
| 40 | +``` |
| 41 | + |
| 42 | +提示: |
| 43 | +* $0 <= poured <= 10^9$ |
| 44 | +* $0 <= query_glass <= query_row < 100$ |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +### 线性 DP |
| 49 | + |
| 50 | +为了方便,我们令 `poured` 为 `k`,`query_row` 和 `query_glass` 分别为 $n$ 和 $m$。 |
| 51 | + |
| 52 | +定义 $f[i][j]$ 为第 $i$ 行第 $j$ 列杯子所经过的水的流量(而不是最终剩余的水量)。 |
| 53 | + |
| 54 | +起始我们有 $f[0][0] = k$,最终答案为 $\min(f[n][m], 1)$。 |
| 55 | + |
| 56 | +不失一般性考虑 $f[i][j]$ 能够更新哪些状态:显然当 $f[i][j]$ 不足 $1$ 的时候,不会有水从杯子里溢出,即 $f[i][j]$ 将不能更新其他状态;当 $f[i][j]$ 大于 $1$ 时,将会有 $f[i][j] - 1$ 的水会等量留到下一行的杯子里,所流向的杯子分别是「第 $i + 1$ 行第 $j$ 列的杯子」和「第 $i + 1$ 行第 $j + 1$ 列的杯子」,增加流量均为 $\frac{f[i][j] - 1}{2}$,即有 $f[i + 1][j] += \frac{f[i][j] - 1}{2}$ 和 $f[i + 1][j + 1] += \frac{f[i][j] - 1}{2}$。 |
| 57 | + |
| 58 | +Java 代码: |
| 59 | +```Java |
| 60 | +class Solution { |
| 61 | + public double champagneTower(int k, int n, int m) { |
| 62 | + double[][] f = new double[n + 10][n + 10]; |
| 63 | + f[0][0] = k; |
| 64 | + for (int i = 0; i <= n; i++) { |
| 65 | + for (int j = 0; j <= i; j++) { |
| 66 | + if (f[i][j] <= 1) continue; |
| 67 | + f[i + 1][j] += (f[i][j] - 1) / 2; |
| 68 | + f[i + 1][j + 1] += (f[i][j] - 1) / 2; |
| 69 | + } |
| 70 | + } |
| 71 | + return Math.min(f[n][m], 1); |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | +TypeScript 代码: |
| 76 | +```TypeScript |
| 77 | +function champagneTower(k: number, n: number, m: number): number { |
| 78 | + const f = new Array<Array<number>>() |
| 79 | + for (let i = 0; i < n + 10; i++) f.push(new Array<number>(n + 10).fill(0)) |
| 80 | + f[0][0] = k |
| 81 | + for (let i = 0; i <= n; i++) { |
| 82 | + for (let j = 0; j <= i; j++) { |
| 83 | + if (f[i][j] <= 1) continue |
| 84 | + f[i + 1][j] += (f[i][j] - 1) / 2 |
| 85 | + f[i + 1][j + 1] += (f[i][j] - 1) / 2 |
| 86 | + } |
| 87 | + } |
| 88 | + return Math.min(f[n][m], 1) |
| 89 | +} |
| 90 | +``` |
| 91 | +Python3 代码: |
| 92 | +```Python |
| 93 | +class Solution: |
| 94 | + def champagneTower(self, k: int, n: int, m: int) -> float: |
| 95 | + f = [[0] * (n + 10) for _ in range(n + 10)] |
| 96 | + f[0][0] = k |
| 97 | + for i in range(n + 1): |
| 98 | + for j in range(i + 1): |
| 99 | + if f[i][j] <= 1: |
| 100 | + continue |
| 101 | + f[i + 1][j] += (f[i][j] - 1) / 2 |
| 102 | + f[i + 1][j + 1] += (f[i][j] - 1) / 2 |
| 103 | + return min(f[n][m], 1) |
| 104 | +``` |
| 105 | +* 时间复杂度:$O(n^2)$ |
| 106 | +* 空间复杂度:$O(n^2)$ |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +### 最后 |
| 111 | + |
| 112 | +这是我们「刷穿 LeetCode」系列文章的第 `No.799` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 113 | + |
| 114 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 115 | + |
| 116 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 117 | + |
| 118 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 119 | + |
0 commit comments