Dãy con tăng dần dài nhất¶
Khái quát¶
Bài toán trình bày dưới đây là một ví dụ về quy hoạch động và có thể được xếp vào dạng bài toán tìm dãy con tăng dần dài nhất (LIS - Longest Increasing Subsequence).
Bài toán được phát biểu sơ nét như sau:
Cho một mảng các số nguyên không có thứ tự. Tìm độ dài của dãy con tăng dần dài nhất. Biết rằng dãy con không nhất thiết phải gồm các phần tử liên tiếp nhau.
Một số bài toán LIS có thể được giải hay hơn theo hướng của thuật toán tìm kiếm nhị phân.
Bài toán¶
Yêu cầu¶
Cho mảng A gồm các số nguyên. Tìm ra dãy con thỏa hai điều kiện: tăng dần và có độ dài dài nhất.
Input¶
Output¶
Giải thích¶
Mảng A có 7 phần tử.
Dãy con tăng dần dài nhất gồm 4 phần tử là -1 0 2 3.
Cách giải đề xuất¶
Xây dựng bảng quy hoạch¶
Bảng quy hoạch D
là mảng một chiều có số phần tử như mảng A
, trong đó D[i]
lưu độ dài của dãy con dài nhất tính từ đầu đến phần tử A[i]
.
Theo đó, D[i]
nào có giá trị lớn nhất thì nó chính là độ dài của dãy con dài nhất cần tìm.
Bước 1: Khởi tạo
Trong trường hợp xấu nhất, mảng A
là giảm dần. Lúc này, độ dài dài nhất của dãy con tăng dần là 1, tức mỗi dãy con chỉ có một phần tử, không có phần tử thứ hai làm cho nó tăng nữa.
Bên cạnh đó, để xuất ra dãy con dài nhất đó, ta khởi tạo mảng trace
để lưu trữ kết nối của các phần tử thuộc cùng một dãy con. Cụ thể, trace[i] = j
, nghĩa là trước phần tử A[i]
là phần tử A[j]
.
Ban đầu, chưa có kết nối nào, ta gán giá trị -1
toàn bộ mảng trace
.
Bước 2: Thực hiện quy hoạch động
Duyệt toàn bộ mảng A
bằng biến i
, lặp thao tác:
-
Duyệt dãy con của mảng
A
bằng biến j trong đoạn[0..i - 1]
, lặp thao tác:-
Xét xem
A[i]
có được "kết nạp" vào dãy con màA[j]
đang có mặt trong đó hay không. Muốn được "kết nạp",A[i]
phải thỏa hai điều kiện sau:- Bảo đảm dãy con của
A[j]
vẫn tăng dần:A[j] < A[i]
. -
Bảo đảm
D[j]
(1) khi thêm một đơn vị (tức thêm một phần tử làA[i]
) thì phải lớn hơnD[i]
(2):D[j] + 1 > D[i]
.D[j]
là độ dài dài nhất của dãy con nào đó tính từ đầu mảngA
đếnj
.D[i]
là độ dài dài nhất của dãy con nào đó tính từ đầu mảngA
đếni
.
- Bảo đảm dãy con của
-
Nếu hai điều kiện này được thỏa thì ta ghi nhận độ dài dài nhất mới và ghi nhận sự kết nối của
A[i]
vàA[j]
vào mảngtrace
.
-
Xuất output¶
Trước hết, ta tìm xem vị trí của phần tử trong mảng D
lưu độ dài dài nhất, gọi là finish
.
Theo bộ test trên, phần tử đó là D[6]
, tức finish == 6
, như bảng dưới đây:
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
A |
3 | 4 | -1 | 0 | 6 | 2 | 3 |
D |
1 | 2 | 1 | 2 | 3 | 3 | 4 |
trace |
-1 | 0 | -1 | 2 | 1 | 3 | 5 |
Dựa vào mảng trace
, ta cho finish
lui dần về phía đầu của mảng A
để xuất ra dãy con có độ dài dài nhất.
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.