-
Notifications
You must be signed in to change notification settings - Fork 956
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
699b1ec
commit f4ac328
Showing
3 changed files
with
121 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
### 题目描述 | ||
|
||
这是 LeetCode 上的 **[799. 香槟塔](https://leetcode.cn/problems/champagne-tower/solution/by-ac_oier-c8jn/)** ,难度为 **中等**。 | ||
|
||
Tag : 「动态规划」、「线性 DP」 | ||
|
||
|
||
|
||
我们把玻璃杯摆成金字塔的形状,其中 第一层 有 `1` 个玻璃杯, 第二层 有 `2` 个,依次类推到第 `100` 层,每个玻璃杯 (`250ml`) 将盛有香槟。 | ||
|
||
从顶层的第一个玻璃杯开始倾倒一些香槟,当顶层的杯子满了,任何溢出的香槟都会立刻等流量的流向左右两侧的玻璃杯。当左右两边的杯子也满了,就会等流量的流向它们左右两边的杯子,依次类推。(当最底层的玻璃杯满了,香槟会流到地板上) | ||
|
||
例如,在倾倒一杯香槟后,最顶层的玻璃杯满了。倾倒了两杯香槟后,第二层的两个玻璃杯各自盛放一半的香槟。在倒三杯香槟后,第二层的香槟满了 - 此时总共有三个满的玻璃杯。在倒第四杯后,第三层中间的玻璃杯盛放了一半的香槟,他两边的玻璃杯各自盛放了四分之一的香槟,如下图所示。 | ||
|
||
 | ||
|
||
现在当倾倒了非负整数杯香槟后,返回第 i 行 j 个玻璃杯所盛放的香槟占玻璃杯容积的比例( i 和 j 都从0开始)。 | ||
|
||
示例 1: | ||
``` | ||
输入: poured(倾倒香槟总杯数) = 1, query_glass(杯子的位置数) = 1, query_row(行数) = 1 | ||
输出: 0.00000 | ||
解释: 我们在顶层(下标是(0,0))倒了一杯香槟后,没有溢出,因此所有在顶层以下的玻璃杯都是空的。 | ||
``` | ||
示例 2: | ||
``` | ||
输入: poured(倾倒香槟总杯数) = 2, query_glass(杯子的位置数) = 1, query_row(行数) = 1 | ||
输出: 0.50000 | ||
解释: 我们在顶层(下标是(0,0)倒了两杯香槟后,有一杯量的香槟将从顶层溢出,位于(1,0)的玻璃杯和(1,1)的玻璃杯平分了这一杯香槟,所以每个玻璃杯有一半的香槟。 | ||
``` | ||
示例 3: | ||
``` | ||
输入: poured = 100000009, query_row = 33, query_glass = 17 | ||
输出: 1.00000 | ||
``` | ||
|
||
提示: | ||
* $0 <= poured <= 10^9$ | ||
* $0 <= query_glass <= query_row < 100$ | ||
|
||
--- | ||
|
||
### 线性 DP | ||
|
||
为了方便,我们令 `poured` 为 `k`,`query_row` 和 `query_glass` 分别为 $n$ 和 $m$。 | ||
|
||
定义 $f[i][j]$ 为第 $i$ 行第 $j$ 列杯子所经过的水的流量(而不是最终剩余的水量)。 | ||
|
||
起始我们有 $f[0][0] = k$,最终答案为 $\min(f[n][m], 1)$。 | ||
|
||
不失一般性考虑 $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}$。 | ||
|
||
Java 代码: | ||
```Java | ||
class Solution { | ||
public double champagneTower(int k, int n, int m) { | ||
double[][] f = new double[n + 10][n + 10]; | ||
f[0][0] = k; | ||
for (int i = 0; i <= n; i++) { | ||
for (int j = 0; j <= i; j++) { | ||
if (f[i][j] <= 1) continue; | ||
f[i + 1][j] += (f[i][j] - 1) / 2; | ||
f[i + 1][j + 1] += (f[i][j] - 1) / 2; | ||
} | ||
} | ||
return Math.min(f[n][m], 1); | ||
} | ||
} | ||
``` | ||
TypeScript 代码: | ||
```TypeScript | ||
function champagneTower(k: number, n: number, m: number): number { | ||
const f = new Array<Array<number>>() | ||
for (let i = 0; i < n + 10; i++) f.push(new Array<number>(n + 10).fill(0)) | ||
f[0][0] = k | ||
for (let i = 0; i <= n; i++) { | ||
for (let j = 0; j <= i; j++) { | ||
if (f[i][j] <= 1) continue | ||
f[i + 1][j] += (f[i][j] - 1) / 2 | ||
f[i + 1][j + 1] += (f[i][j] - 1) / 2 | ||
} | ||
} | ||
return Math.min(f[n][m], 1) | ||
} | ||
``` | ||
Python3 代码: | ||
```Python | ||
class Solution: | ||
def champagneTower(self, k: int, n: int, m: int) -> float: | ||
f = [[0] * (n + 10) for _ in range(n + 10)] | ||
f[0][0] = k | ||
for i in range(n + 1): | ||
for j in range(i + 1): | ||
if f[i][j] <= 1: | ||
continue | ||
f[i + 1][j] += (f[i][j] - 1) / 2 | ||
f[i + 1][j + 1] += (f[i][j] - 1) / 2 | ||
return min(f[n][m], 1) | ||
``` | ||
* 时间复杂度:$O(n^2)$ | ||
* 空间复杂度:$O(n^2)$ | ||
|
||
--- | ||
|
||
### 最后 | ||
|
||
这是我们「刷穿 LeetCode」系列文章的第 `No.799` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 | ||
|
||
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 | ||
|
||
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 | ||
|
||
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 | ||
|