Skip to content

Commit a276785

Browse files
committed
Add unique paths
1 parent 8efa517 commit a276785

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Bài Toán Đường Đi Đơn Nhất
2+
3+
Một con robot ở vị trí trên cùng bên trái của lưới `m x n` (đánh dấu 'Start' ở biểu đồ bên dưới).
4+
5+
Robot có thể đi xuống hoặc qua phải đến bất kỳ điểm nào. Robot sẽ đi đến vị trí dưới cùng bên phải của lưới (đánh dấu 'Finish' ở biểu đồ bên dưới).
6+
7+
Có bao nhiều đường đi đơn nhất?
8+
9+
![Unique Paths](https://leetcode.com/static/images/problemset/robot_maze.png)
10+
11+
## Ví dụ
12+
13+
**Ví dụ 1**
14+
15+
```
16+
Input: m = 3, n = 2
17+
Output: 3
18+
Giải thích:
19+
Từ góc trên trái, ta có ba cách để tới góc dưới phải:
20+
1. Phải -> Phải -> Xuống
21+
2. Phải -> Xuống -> Phải
22+
3. Xuống -> Phải -> Phải
23+
```
24+
25+
**Ví dụ 2**
26+
27+
```
28+
Input: m = 7, n = 3
29+
Output: 28
30+
```
31+
32+
## Giải thuật
33+
34+
### Quay lùi
35+
36+
Ý tưởng đầu tiên là ta sẽ xây dựng một cây lựa chọn trong đó `D` có nghĩa là đi xuống và `R` có nghĩa là sang phải. Ví dụ trong trường bảng `width = 3``height = 2` ta sẽ có cây lựa chọn sau:
37+
38+
```
39+
START
40+
/ \
41+
D R
42+
/ / \
43+
R D R
44+
/ / \
45+
R R D
46+
47+
END END END
48+
```
49+
50+
Ta có thể thấy ba đường đi đơn nhất để trả lời cho vấn đề trên.
51+
52+
**Độ phức tạp thời gian**: `O(2 ^ n)` - gần với trường hợp xấu nhất bảng vuông có kích thước `n`.
53+
54+
**Độ phức tạp không gian mở rộng**: `O(m + n)` - vì ta cần lưu trữ đường đi hiện tại với các vị trí.
55+
56+
### Quy hoạch động
57+
58+
Hãy xem `BOARD[i][j]` là vấn đề phụ của ta.
59+
60+
Vì ta có giới hạn chỉ được di chuyển sang phải và xuống, nên ta có thể nói rằng số lượng đường đi đơn nhất đến ô hiện tại là tổng số đường đi đơn nhất đến ô phía trên ô hiện tại và đến ô bên trái ô hiện tại.
61+
62+
```
63+
BOARD[i][j] = BOARD[i - 1][j] + BOARD[i][j - 1]; // vì ta chỉ có thể đi xuống hoặc sang phải.
64+
```
65+
66+
Các trường hợp cơ bản là:
67+
68+
```
69+
BOARD[0][any] = 1; // chỉ có một cách đi đến bất kỳ ô nào ở hàng trên cùng.
70+
BOARD[any][0] = 1; // chỉ có một cách đi đến bất kỳ ô nào ở cột ngoài cùng bên trái.
71+
```
72+
73+
Ví dụ với bảng `3 x 2` ta có ma trận quy hoạch động như sau:
74+
75+
| | 0 | 1 | 1 |
76+
|:---:|:---:|:---:|:---:|
77+
|**0**| 0 | 1 | 1 |
78+
|**1**| 1 | 2 | 3 |
79+
80+
Mỗi ô chứa số lượng đường đi đơn nhất đến nó. Ở dưới cùng bên phải ta có số `3`.
81+
82+
**Độ phức tạp thời gian**: `O(m * n)` - vì ta đi qua từng ô của ma trận quy hoạch động
83+
84+
**Độ phức tạp không gian mở rộng**: `O(m * n)` - vì ta cần ma trận quy hoạch động
85+
86+
### Tam giác Pascal
87+
88+
Bài toán này thực chất là một dạng khác của Tam giác Pascal.
89+
90+
Góc của hình chữ nhật này nằm ở dòng `m + n - 2` và ở vị trí `min (m, n) - 1` của tam giác Pascal.
91+
92+
## Liên kết
93+
94+
- [LeetCode](https://leetcode.com/problems/unique-paths/description/)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Giải pháp QUAY LÙI để giải bài toán Đường Đi Đơn Nhất.
3+
*
4+
* @param {number} width - Chiều dài của bảng.
5+
* @param {number} height - Chiểu rộng của bảng.
6+
* @param {number[][]} steps - Các bước đã được thực hiện.
7+
* @param {number} uniqueSteps - Tổng số bước đơn nhất.
8+
* @return {number} - Số đường đi đơn nhất.
9+
*/
10+
export default function btUniquePaths(width, height, steps = [[0, 0]], uniqueSteps = 0) {
11+
// Tìm nạp vị trí hiện tại trên bảng.
12+
const currentPos = steps[steps.length - 1];
13+
14+
// Kiểm tra nếu ta đi đến cuối.
15+
if (currentPos[0] === width - 1 && currentPos[1] === height - 1) {
16+
// Trong trường hợp nếu ta đã hoàn thành,
17+
// hãy tăng tổng số bước duy nhất.
18+
return uniqueSteps + 1;
19+
}
20+
21+
// Hãy tính xem ta sẽ có bao nhiêu con đường đơn nhất
22+
// bằng cách đi sang phải và đi xuống.
23+
let rightUniqueSteps = 0;
24+
let downUniqueSteps = 0;
25+
26+
// Sang phải nếu có thể.
27+
if (currentPos[0] < width - 1) {
28+
steps.push([
29+
currentPos[0] + 1,
30+
currentPos[1],
31+
]);
32+
33+
// Tính xem ta sẽ đi được bao nhiêu đường đơn nhất bằng cách di chuyển sang phải.
34+
rightUniqueSteps = btUniquePaths(width, height, steps, uniqueSteps);
35+
36+
// QUAY LÙI và thực hiên bước khác.
37+
steps.pop();
38+
}
39+
40+
// Đi xuống nếu có thể.
41+
if (currentPos[1] < height - 1) {
42+
steps.push([
43+
currentPos[0],
44+
currentPos[1] + 1,
45+
]);
46+
47+
// Tính xem ta sẽ đi được bao nhiêu đường đơn nhất bằng cách di chuyển xuống.
48+
downUniqueSteps = btUniquePaths(width, height, steps, uniqueSteps);
49+
50+
// QUAY LÙI và thực hiên bước khác.
51+
steps.pop();
52+
}
53+
54+
// Tổng số bước đơn nhất sẽ bằng tổng số bước đơn nhất bằng cách
55+
// sang phải cộng với tổng số bước đơn nhất bằng cách đi xuống.
56+
return rightUniqueSteps + downUniqueSteps;
57+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Giải pháp QUY HOẠCH ĐỘNG để giải bài toán Đường Đi Đơn Nhất.
3+
*
4+
* @param {number} width - Chiều dài của bảng.
5+
* @param {number} height - Chiểu rộng của bảng.
6+
* @return {number} - Số đường đi đơn nhất.
7+
*/
8+
export default function dpUniquePaths(width, height) {
9+
// Khởi tạo bảng.
10+
const board = Array(height).fill(null).map(() => {
11+
return Array(width).fill(0);
12+
});
13+
14+
// Trường hợp cơ bản.
15+
// Chỉ có một cách để đến board[0][any] và cũng
16+
// chỉ có một cách để đến board[any][0]. Điều này là do ta bị hạn chế
17+
// chỉ di chuyển sang phải và xuống.
18+
for (let rowIndex = 0; rowIndex < height; rowIndex += 1) {
19+
for (let columnIndex = 0; columnIndex < width; columnIndex += 1) {
20+
if (rowIndex === 0 || columnIndex === 0) {
21+
board[rowIndex][columnIndex] = 1;
22+
}
23+
}
24+
}
25+
26+
// Bây giờ, vì ta bị hạn chế là chỉ di chuyển sang phải và xuống,
27+
// ta có thể nói rằng số lượng đường đi đơn nhất đến ô hiện tại
28+
//là tổng số đường đi đơn nhất đến ô phía trên ô hiện tại
29+
// và đến ô bên trái của hiện tại.
30+
for (let rowIndex = 1; rowIndex < height; rowIndex += 1) {
31+
for (let columnIndex = 1; columnIndex < width; columnIndex += 1) {
32+
const uniquesFromTop = board[rowIndex - 1][columnIndex];
33+
const uniquesFromLeft = board[rowIndex][columnIndex - 1];
34+
board[rowIndex][columnIndex] = uniquesFromTop + uniquesFromLeft;
35+
}
36+
}
37+
38+
return board[height - 1][width - 1];
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pascalTriangle from '../../math/pascal-triangle/pascalTriangle';
2+
3+
/**
4+
* @param {number} width
5+
* @param {number} height
6+
* @return {number}
7+
*/
8+
export default function uniquePaths(width, height) {
9+
const pascalLine = width + height - 2;
10+
const pascalLinePosition = Math.min(width, height) - 1;
11+
12+
return pascalTriangle(pascalLine)[pascalLinePosition];
13+
}

0 commit comments

Comments
 (0)