LeetCode 0403
0403. Frog Jump
- Nhãn: Mảng, Quy hoạch động
- Độ khó: Khó
Tóm tắt đề bài
Mô tả: Một con ếch muốn băng qua sông, sông được chia thành các ô, mỗi ô có thể chứa một viên đá (hoặc không có đá). Con ếch chỉ có thể nhảy vào các ô có đá, không thể nhảy vào các ô không có đá.
Cho một mảng stones
được sắp xếp theo thứ tự tăng dần, trong đó stones[i]
đại diện cho số thứ tự của viên đá thứ i
. Giả sử số thứ tự của viên đá đầu tiên là 0
(tức stones[0] == 0
).
Ban đầu, con ếch đứng trên viên đá thứ nhất (tức stones[0]
), và chỉ có thể nhảy 1
đơn vị (tức chỉ có thể nhảy từ ô thứ 0
đến ô thứ 1
).
Nếu con ếch nhảy về phía trước k
đơn vị ở bước trước thì nó chỉ có thể nhảy về phía trước k - 1
, k
hoặc k + 1
đơn vị ở bước tiếp theo.
Yêu cầu: Xác định xem con ếch có thể băng qua sông thành công không (tức có thể nhảy lên viên đá cuối cùng không). Nếu có, trả về True
; nếu không, trả về False
.
Giới hạn:
- .
- .
- .
- Mảng
stones
được sắp xếp theo thứ tự tăng dần.
Ví dụ:
- Ví dụ 1:
Input: stones = [0,1,3,5,6,8,12,17]
Output: true
Explanation: Con ếch có thể băng qua sông thành công bằng cách nhảy như sau: nhảy `1` đơn vị đến viên đá thứ `2`, sau đó nhảy `2` đơn vị đến viên đá thứ `3`, tiếp theo nhảy `2` đơn vị đến viên đá thứ `4`, sau đó nhảy `3` đơn vị đến viên đá thứ `6`, nhảy `4` đơn vị đến viên đá thứ `7`, cuối cùng, nhảy `5` đơn vị đến viên đá thứ `8` (tức viên đá cuối cùng).
Ý tưởng giải quyết
Ý tưởng 1: Quy hoạch động
Theo đề bài, nếu con ếch nhảy k
đơn vị từ ô trước đó, thì ở bước tiếp theo, con ếch chỉ có thể nhảy k - 1
, k
hoặc k + 1
đơn vị.
Chúng ta có thể sử dụng quy hoạch động để xác định xem con ếch có thể nhảy đến ô cuối cùng hay không. Ta có thể chia bài toán thành các giai đoạn, định nghĩa trạng thái và tìm công thức chuyển trạng thái.
1. Chia giai đoạn
Chia giai đoạn dựa trên số lượng viên đá.
2. Định nghĩa trạng thái
Định nghĩa trạng thái dp[i][k]
để biểu diễn: con ếch có thể nhảy k
đơn vị để đến viên đá thứ i
hay không.
3. Công thức chuyển trạng thái
- Vòng lặp bên ngoài duyệt qua từng viên đá
i
, với mỗi viên đái
, sử dụng vòng lặp bên trong duyệt qua tất cả các viên đáj
trước đó. - Tính khoảng cách từ viên đá
j
trước đó đến viên đái
hiện tại làk
. - Nếu con ếch có thể nhảy từ viên đá
j
trước đó đến viên đái
hiện tại bằng cách nhảyk - 1
,k
hoặck + 1
đơn vị, thì con ếch cũng có thể nhảy từ viên đáj
đến viên đái
bằng cách nhảyk
đơn vị. Điều này có nghĩa là, nếudp[j][k - 1]
hoặcdp[j][k]
hoặcdp[j][k + 1]
làTrue
, thìdp[i][k]
cũng làTrue
.- Tức là:
dp[i][k] = dp[j][k - 1] or dp[j][k] or dp[j][k + 1]
.
- Tức là:
4. Điều kiện ban đầu
Ban đầu, con ếch đứng trên viên đá đầu tiên (tức stones[0]
), và chỉ có thể nhảy 0
đơn vị để đến viên đá đầu tiên, nên dp[0][0] = True
.
5. Kết quả cuối cùng
Dựa trên định nghĩa trạng thái, dp[i][k]
biểu diễn: con ếch có thể nhảy k
đơn vị để đến viên đá thứ i
hay không. Nếu dp[size - 1][k]
là True
, thì con ếch có thể nhảy đến viên đá cuối cùng thành công (tức có thể nhảy lên viên đá cuối cùng). Nếu không, thì con ếch không thể nhảy đến viên đá cuối cùng.
Ý tưởng 1: Cài đặt quy hoạch động
class Solution:
def canCross(self, stones: List[int]) -> bool:
n = len(stones)
dp = [[False] * (n + 1) for _ in range(n)]
dp[0][0] = True
for i in range(n):
for j in range(i):
k = stones[i] - stones[j]
if k <= 0 or k > j + 1:
continue
dp[i][k] = dp[j][k - 1] or dp[j][k] or dp[j][k + 1]
if dp[n - 1][k]:
return True
return False
Ý tưởng 1: Độ phức tạp
- Độ phức tạp thời gian: . Vòng lặp lồng nhau có độ phức tạp , do đó tổng độ phức tạp thời gian là .
- Độ phức tạp không gian: . Sử dụng một mảng hai chiều để lưu trạng thái, do đó tổng độ phức tạp không gian là .