Skip to content

Commit fd3c62a

Browse files
committed
Add rain terraces
1 parent 3778d65 commit fd3c62a

File tree

4 files changed

+197
-14
lines changed

4 files changed

+197
-14
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,38 +147,38 @@ Mô hình thuật toán là một phương pháp hoặc cách tiếp cận chung
147147

148148
* **Phá mã Brute Force** - xem xét tất cả các khả năng và chọn giải pháp tốt nhất
149149
* `B` [Linear Search](src/algorithms/search/linear-search)
150-
* `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces)
151-
* `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase)
150+
* `B` [Rain Terraces](src/algorithms/others/rain-terraces)
151+
* `B` [Recursive Staircase](src/algorithms/others/recursive-staircase)
152152
* `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
153153
* `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman)
154154
* `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - biến đổi Fourier.
155155
* **Giải thuật tham lam** - chọn phương án tốt nhất tại thời điểm hiện tại mà không cần cân nhắc đến các lựa chọn khác trong tương lai.
156-
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
156+
* `B` [Jump Game](src/algorithms/others/jump-game)
157157
* `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
158158
* `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra)
159159
* `A` [Prim’s Algorithm](src/algorithms/graph/prim)
160160
* `A` [Kruskal’s Algorithm](src/algorithms/graph/kruskal)
161161
* **Giải thuật chia để trị** - chia nhỏ vấn đề và giải quyết các phần nhỏ đấy.
162162
* `B` [Binary Search](src/algorithms/search/binary-search)
163-
* `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
163+
* `B` [Tower of Hanoi](src/algorithms/others/hanoi-tower)
164164
* `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
165165
* `B` [Euclidean Algorithm](src/algorithms/math/euclidean-algorithm)
166166
* `B` [Merge Sort](src/algorithms/sorting/merge-sort)
167167
* `B` [Quicksort](src/algorithms/sorting/quick-sort)
168168
* `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
169169
* `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
170170
* `B` [Matrices](src/algorithms/math/matrix) -tạo và duyệt các ma trận có hình dạng khác nhau.
171-
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
171+
* `B` [Jump Game](src/algorithms/others/jump-game)
172172
* `B` [Fast Powering](src/algorithms/math/fast-powering)
173-
* `B` [Best Time To Buy Sell Stocks](src/algorithms/uncategorized/best-time-to-buy-sell-stocks)
173+
* `B` [Best Time To Buy Sell Stocks](src/algorithms/others/best-time-to-buy-sell-stocks)
174174
* `A` [Permutations](src/algorithms/sets/permutations)
175175
* `A` [Combinations](src/algorithms/sets/combinations)
176176
* **Quy hoạch động** - xây dựng một giải pháp bằng cách sử dụng các giải pháp phụ đã tìm thấy trước đây.
177177
* `B` [Fibonacci Number](src/algorithms/math/fibonacci)
178-
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
179-
* `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
180-
* `B` [Rain Terraces](src/algorithms/uncategorized/rain-terraces)
181-
* `B` [Recursive Staircase](src/algorithms/uncategorized/recursive-staircase)
178+
* `B` [Jump Game](src/algorithms/others/jump-game)
179+
* `B` [Unique Paths](src/algorithms/others/unique-paths)
180+
* `B` [Rain Terraces](src/algorithms/others/rain-terraces)
181+
* `B` [Recursive Staircase](src/algorithms/others/recursive-staircase)
182182
* `B` [Seam Carving](src/algorithms/image-processing/seam-carving) -
183183
* `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance)
184184
* `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
@@ -192,12 +192,12 @@ Mô hình thuật toán là một phương pháp hoặc cách tiếp cận chung
192192
* `A` [Floyd-Warshall Algorithm](src/algorithms/graph/floyd-warshall)
193193
* `A` [Regular Expression Matching](src/algorithms/string/regular-expression-matching)
194194
* **Giải thuật quay lùi** - tương tự brute force ta cũng tìm tất cả các khả năng nhưng trong quá trình tìm kiếm, nếu ta gặp một hướng lựa chọn không thỏa mãn, ta quay lui về điểm lựa chọn nơi có các hướng khác và thử hướng lựa chọn tiếp theo. Khi đã thử hết các lựa chọn xuất phát từ điểm lựa chọn đó, ta quay lại điểm lựa chọn trước đó và thử hướng lựa chọn tiếp theo tại đó. Quá trình tìm kiếm thất bại khi không còn điểm lựa chọn nào nữa
195-
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
196-
* `B` [Unique Paths](src/algorithms/uncategorized/unique-paths)
195+
* `B` [Jump Game](src/algorithms/others/jump-game)
196+
* `B` [Unique Paths](src/algorithms/others/unique-paths)
197197
* `B` [Power Set](src/algorithms/sets/power-set)
198198
* `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle)
199-
* `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
200-
* `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
199+
* `A` [N-Queens Problem](src/algorithms/others/n-queens)
200+
* `A` [Knight's Tour](src/algorithms/others/knight-tour)
201201
* `A` [Combination Sum](src/algorithms/sets/combination-sum)
202202
* **Giải thuật nhánh cận** - ghi nhớ giải pháp chi phí thấp nhất được tìm thấy ở mỗi giai đoạn của tìm kiếm quay lùi, và sử dụng chi phí của giải pháp chi phí thấp nhất được tìm thấy như là một ràng buộc thấp hơn về chi phí của một giải pháp chi phí thấp nhất cho vấn đề, để loại bỏ các giải pháp với chi phí lớn hơn giải pháp chi phí thấp nhất được tìm thấy cho đến nay.
203203

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Bài Toán Thềm Nước Mưa
2+
3+
Cho một mảng số nguyên không âm biểu diễn các bậc thang trong bản đồ độ cao trong đó chiều rộng mỗi bậc là `1` đơn vị, hãy tính lượng nước còn lại trong thềm sau khi mưa.
4+
5+
![Rain Terraces](https://www.geeksforgeeks.org/wp-content/uploads/watertrap.png)
6+
7+
## Ví dụ
8+
9+
**Ví dụ 1**
10+
11+
```
12+
Input: arr[] = [2, 0, 2]
13+
Output: 2
14+
Cấu trúc thềm như sau:
15+
16+
| |
17+
|_|
18+
19+
Lượng nước đọng lại trong thềm là 2 đơn vị.
20+
```
21+
22+
**Ví dụ 2**
23+
24+
```
25+
Input: arr[] = [3, 0, 0, 2, 0, 4]
26+
Output: 10
27+
Cấu trúc thềm như sau:
28+
29+
|
30+
| |
31+
| | |
32+
|__|_|
33+
34+
Lượng nước đọng lại lúc này giữa bậc 3 và 2 là "3*2" đơn vị, trên đỉnh bậc 2 là "1" đơn vị, và giữa 2 và 4 là là "3" đơn vị. Tổng cộng là 10.
35+
36+
```
37+
38+
**Ví dụ 3**
39+
40+
41+
```
42+
Input: arr[] = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
43+
Output: 6
44+
Cấu trúc thềm như sau:
45+
46+
|
47+
| || |
48+
_|_||_||||||
49+
50+
Đọng "1" đơn vị giữa bậc 1 và 2, "4" đơn vị giữa bậc 2 và 3, và "1" đơn vị giữa 2 bậc 2.
51+
```
52+
53+
## Giải thuật
54+
55+
Một phần tử của mảng có thể đọng nước nếu có bậc cao hơn ở bên trái và phải. Ta có thể tìm lương nước đọng trong mọi phần tử bằng cách tìm chiều cao của các bậc bên trái và phải. Ý tưởng là tính toán lượng nước có thể lưu trữ trong mọi phần tử của mảng, ví dụ ta có mảng `[3, 0, 0, 2, 0, 4]`, ta có thể đọng "3*2" đơn vị giữa bậc 3 và 3, "1" đơn vị trên đỉnh bậc 2 và "3" đơn vị giữa 2 và 4. Xem sơ đồ dưới đây.
56+
57+
58+
### Giải pháp 1: Phá mã
59+
60+
Với từng phần tử trong mảng, ta tìm lượng nước lớn nhất có thể đọng sau mưa, bằng mức tối thiểu của chiều cao tối đa của các bậc ở cả hai bên trừ đi chiều cao của chính nó.
61+
62+
**Bước**
63+
64+
- Khởi tạo `answer = 0`
65+
- Lặp lại từ trái sang phải mảng:
66+
- Khởi tạo `max_left = 0``max_right = 0`
67+
- Lặp lại từ phần tử hiện tại đến khi phần bắt đầu thay đổi của mảng: `max_left = max(max_left, height[j])`
68+
- Lặp lại từ phần tử hiện tại đến phần kết thúc thay đổi của mảng: `max_right = max(max_right, height[j])`
69+
- Thêm `min(max_left, max_right) - height[i]` vào `answer`
70+
71+
**Phân tích độ phức tạp**
72+
73+
Độ phức tạp thời gian: `O(n^2)`. Với từng phần tử, ta phải lặp phần bên trái và bên phải.
74+
75+
Độ phức tạp không gian mở rộng: `O(1)` không gian mở rộng
76+
77+
### Giải pháp 2: Quy hoạch động
78+
79+
Với giải pháp phá mã, ta phải lặp từ bên trái và bên phải mỗi lần để tìm ra bậc cao nhất tính đến chỉ số đó. Nhưng ta có thể lưu trữ chúng với quy hoạch động.
80+
81+
Với quy hoạch động ta có thể tính toán trước bậc cao nhất ở bên trái và bên phải của mọi bậc trong thời gian `O (n)`. Sau đó, sử dụng các giá trị được tính toán trước này để tìm lượng nước trong mọi phần tử mảng.
82+
83+
Khái niệm có thể mình hoạ như sau:
84+
85+
![DP Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/Figures/42/trapping_rain_water.png)
86+
87+
**Bước**
88+
89+
- Tìm bậc cao nhất từ bên trái của chỉ số `i` trong mảng `left_max`
90+
- Tìm bậc cao nhất từ bên phải của chỉ số `i` trong mảng `right_max`
91+
- Lặp lại mảng `height` và cập nhật `answer`:
92+
- Thêm `min(max_left[i], max_right[i]) - height[i]` vào `answer`.
93+
94+
**Phân tích độ phức tạp**
95+
96+
Độ phức tạp thời gian: `O(n)`. Ta lưu trữ độ cao lớn nhất đến một điểm bằng cách dùng 2 lần lặp lại `O(n)`. Cuối cùng cập nhật `answer` bằng giá trị đã lưu ở `O(n)`.
97+
98+
Độ phức tạp không gian: `O(n)` không gian mở rộng. Bổ sung không gian cho mảng `left_max``right_max` so với giải pháp 1.
99+
100+
## Liên kết
101+
102+
- [GeeksForGeeks](https://www.geeksforgeeks.org/trapping-rain-water/)
103+
- [LeetCode](https://leetcode.com/problems/trapping-rain-water/solution/)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Giải pháp PHÁ MÃ để giải quyết bài toán Thềm Nước Mưa.
3+
*
4+
* @param {number[]} terraces
5+
* @return {number}
6+
*/
7+
export default function bfRainTerraces(terraces) {
8+
let waterAmount = 0;
9+
10+
for (let terraceIndex = 0; terraceIndex < terraces.length; terraceIndex += 1) {
11+
// Lấy bậc cao nhất bên trái.
12+
let leftHighestLevel = 0;
13+
for (let leftIndex = terraceIndex - 1; leftIndex >= 0; leftIndex -= 1) {
14+
leftHighestLevel = Math.max(leftHighestLevel, terraces[leftIndex]);
15+
}
16+
17+
// Lấy bậc cao nhất bên phải.
18+
let rightHighestLevel = 0;
19+
for (let rightIndex = terraceIndex + 1; rightIndex < terraces.length; rightIndex += 1) {
20+
rightHighestLevel = Math.max(rightHighestLevel, terraces[rightIndex]);
21+
}
22+
23+
// Thêm lượng nước trên thềm hiện tại.
24+
const terraceBoundaryLevel = Math.min(leftHighestLevel, rightHighestLevel);
25+
if (terraceBoundaryLevel > terraces[terraceIndex]) {
26+
// Thềm có thể đọng nước nếu phần thấp nhất của hai bậc cao nhất
27+
// bên trái và phải vẫn cao hơn bậc hiện tại
28+
waterAmount += terraceBoundaryLevel - terraces[terraceIndex];
29+
}
30+
}
31+
32+
return waterAmount;
33+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Giải pháp QUY HOẠCH ĐỘNG để giải quyết bài toán Thềm Nước Mưa.
3+
*
4+
* @param {number[]} terraces
5+
* @return {number}
6+
*/
7+
export default function dpRainTerraces(terraces) {
8+
let waterAmount = 0;
9+
10+
// Khởi tạo mảng lưu trữ danh sách bậc cao nhất bên trái và phải của mỗi vị trí cụ thể.
11+
const leftMaxLevels = new Array(terraces.length).fill(0);
12+
const rightMaxLevels = new Array(terraces.length).fill(0);
13+
14+
// Tính bậc cao nhất từ bên TRÁI cho đến bậc hiện tại.
15+
[leftMaxLevels[0]] = terraces;
16+
for (let terraceIndex = 1; terraceIndex < terraces.length; terraceIndex += 1) {
17+
leftMaxLevels[terraceIndex] = Math.max(
18+
terraces[terraceIndex],
19+
leftMaxLevels[terraceIndex - 1],
20+
);
21+
}
22+
23+
// Tính bậc cao nhất từ bên PHẢI cho đến bậc hiện tại.
24+
rightMaxLevels[terraces.length - 1] = terraces[terraces.length - 1];
25+
for (let terraceIndex = terraces.length - 2; terraceIndex >= 0; terraceIndex -= 1) {
26+
rightMaxLevels[terraceIndex] = Math.max(
27+
terraces[terraceIndex],
28+
rightMaxLevels[terraceIndex + 1],
29+
);
30+
}
31+
32+
// Ta hãy lần lượt đi qua tất cả các bậc và tính toán lượng nước mà mỗi
33+
// bậc có thể đọng dựa trên các giá trị đã tính toán trước đó.
34+
for (let terraceIndex = 0; terraceIndex < terraces.length; terraceIndex += 1) {
35+
// Chọn bậc thấp nhất từ các bậc cao nhất bên trái/bên phải.
36+
const currentTerraceBoundary = Math.min(
37+
leftMaxLevels[terraceIndex],
38+
rightMaxLevels[terraceIndex],
39+
);
40+
41+
if (currentTerraceBoundary > terraces[terraceIndex]) {
42+
waterAmount += currentTerraceBoundary - terraces[terraceIndex];
43+
}
44+
}
45+
46+
return waterAmount;
47+
}

0 commit comments

Comments
 (0)