Nhập môn Quy hoạch động
Nguồn: Topcoder.
Có rất nhiều bài toán được áp dụng quy hoạch động (QHĐ) (Dynamic Programming). QHĐ là một trong những kĩ thuật quan trọng. Bài viết này sẽ giúp bạn hiểu được QHĐ thông qua các ví dụ cụ thể.
Note: Trong bài này có thể có nhiều phần bạn đã biết, bạn hoàn toàn có thể chuyển qua đọc phần khác.
Beginner¶
QHĐ là gì ?¶
QHĐ là kĩ thuật được được dùng khi có một công thức và một (hoặc một vài) trạng thái bắt đầu. Một bài toán được tính bởi các bài toán nhỏ hơn đã tìm ra trước đó. QHĐ có độ phức tạp đa thức nên sẽ chạy nhanh hơn quay lui và duyệt trâu.
Để hiểu rõ hơn hãy xem ví dụ sau:
Cho
đồng xu và giá tiền của mỗi đồng ( ), và số . Tìm số đồng xu nhỏ nhất để tổng giá trị của chúng bằng (số lượng đồng xu không giới hạn).
Bây giờ chúng ta sẽ xây dựng thuật giải:
Đầu tiên, cần tìm một trạng thái của bài toán.
Trạng thái là gì ?¶
Trạng thái là một trường hợp, một bài toán con của bài toán lớn.
Ví dụ, trạng thái trong bài này là số lượng xu nhỏ nhất để tổng bằng
Làm thế nào để tìm được ?¶
Với mỗi
Sau đây là ví dụ:
Cho các đồng xu với giá tiền 1, 3 và 5.
Và
Đầu tiên, ta bắt đầu từ trạng thái 0, chúng ta có
Mã giả:
Gán Min[i] bằng dương vô cùng với mọi i
Min[0]=0
For i = 1 to S
For j = 0 to N - 1
If (Vj<=i AND Min[i-Vj]+1<Min[i])
Then Min[i]=Min[i-Vj]+1
Output Min[S]
Đây là lời giải cho tất cả các tổng:
Tổng | Lượng xu nhỏ nhất | Xu được chọn (tổng còn lại) |
0 | 0 | - |
1 | 1 | 1 (0) |
2 | 2 | 1 (1) |
3 | 1 | 3 (0) |
4 | 2 | 1 (3) |
5 | 1 | 5 (0) |
6 | 2 | 3 (3) |
7 | 3 | 1 (6) |
8 | 2 | 3 (5) |
9 | 3 | 1 (8) |
10 | 2 | 5 (5) |
11 | 3 | 1 (10) |
Vậy là chúng ta đã tìm được lời giải cho 3 đồng xu tổng bằng 11.
Dựa vào bảng trên, ta có thể truy vết lại được những đồng xu nào được chọn để tối ưu bài toán.
Bài QHĐ trên còn có một cách tiếp cận khác nữa. Lần này, ta sẽ không tính liên tiếp các tổng. Bắt đầu từ trạng thái 0. Thử nhét đồng xu thứ 1 vào các tổng đã tính. Nếu như tổng
Elementary¶
Bây giờ, chúng ta cùng đến một khái niệm mới, công thức truy hồi (recurrent relation), mối liên hệ giữa những trạng thái.
Ví dụ:
Cho một dãy N số -
Ta quy định trạng thái
Hãy xem bảng sau với dãy: 5, 3, 4, 8, 6, 7:
I | Độ dài dãy con không giảm dài nhất của i số đầu tiên |
Vị trí của kí tự cuối trong dãy |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 2 |
4 | 3 | 3 |
5 | 3 | 3 |
6 | 4 | 5 |
Bài luyện tập:
Cho đồ thị vô hướng
Các bài ví dụ khác:
- ZigZag – 2003 TCCC Semifinals 3.
- BadNeighbors – 2004 TCCC Round 4.
- FlowerGarden – 2004 TCCC Round 1.
Intermediate¶
Tới đây bạn sẽ được làm quen với QHĐ 2 chiều.
Bài toán:
Cho một bảng
Cách giải bài này cũng tương tự như những bài trước.
Đầu tiên là phải xác định trạng thái là gì. Ở mỗi ô có nhiều nhất 2 cách có thể tới được ô đó, từ ô bên trái và ô phía trên. Do vậy, để tìm trạng thái hiện tại, ta phải tính trước các ô có thể đến được nó.
Ta có công thức truy hồi sau:
Mã giả:
For i = 0 to N - 1
For j = 0 to M - 1
S[i][j] = A[i][j] +
max(S[i][j-1], if j>0 ; S[i-1][j], if i>0 ; 0)
Output S[n-1][m-1]
Ví dụ khác:
- AvoidRoads – 2003 TCO Semifinals 4
- ChessMetric – 2003 TCCC Round 4
Upper-Intermediate¶
Phần này sẽ giới thiệu với bạn những bài toán cùng với một số điều kiện.
Đây là một ví dụ cụ thể:
Cho đồ thị vô hướng
Ban đầu bạn có số tiền là
Có thể dễ dàng thấy đây là một bài Dijkstra cơ bản, tuy nhiên chỉ khác ở chỗ nó có thêm một điều kiện. Trong bài toán Dijkstra cơ bản ta có
Mã giả:
Gán mọi(i,j) là chưa thăm
Gán Min[i][j] bằng dương vô cùng với mọi (i,j)
Min[0][M]=0
While(TRUE)
Trong số những trạng thái chưa thăm (i,j) tìm cái có Min[i][j]
nhỏ nhất. Giải sử nó là (k,l).
Nếu không tìm được (k,l) nào mà Min[k][l] nhỏ hơn dương vô cùng - thoát vòng lặp.
Đánh dấu (k,l) đã thăm
For All Neighbors p of Vertex k.
If (l-S[p]>=0 AND
Min[p][l-S[p]]>Min[k][l]+Dist[k][p])
Then Min[p][l-S[p]]=Min[k][l]+Dist[k][p]
i.e.
Nếu tại (i,j) có đủ tiền để đi qua p (l-S[p] là số tiền còn lại sau khi đi qua p), và đường đi ngắn nhất của (p,l-S[p]) lớn hơn [đường đi ngắn nhất tới (k,l)] + [khoảng cách từ k tới p)],
thì gán (i,j) bằn tổng này.
End For
End While
Tìm số nhỏ nhất trong các Min[N-1][j] (for all j, 0<=j<=M);
Nếu có nhiều hơn một trạng thái, lấy trạng thái nào có j lớn nhất. Nếu không có (N-1,j) nào nhỏ hơn dương vô cùng - không tồn tại đường đi.
Các bài luyện tập:
- Jewelry – 2003 TCO Online Round 4
- StripePainter – SRM 150 Div 1
- QuickSums – SRM 197 Div 2
- ShortPalindromes – SRM 165 Div 2
Advanced¶
Những bài sau đây sẽ cần một chút kĩ năng phân tích để có thể tối ưu chúng thành bài QHĐ.
Problem StarAdventure – SRM 208 Div 1:
Cho ma trận M hàng, N cột (
Giới hạn:
Đọc đến đây, hẳn bạn sẽ thấy cái đề này quen quen, nó chính là bài mở rộng của bài toán phần Intermediate. Ta có thể thử đưa bài toán này về thành bài toán trên. Để ý thấy đường đi từ ô góc phải dưới lên trái trên cũng có thể coi là một đường đi từ góc trái trên xuống. Như vậy, chúng ta phải xử lý bài toán với 3 đường đi từ trái trên xuống. Gọi 3 đường này là trái, giữa và phải. Khi 2 đường giao nhau (như hình dưới):
thì nó cũng tương đương với hình sau:
Bằng cách này, chúng ta đã có một cái nhìn khác về bài toán. Các đường này sẽ không giao nhau (trừ ô góc trái trên và phải dưới). Với mỗi hàng y (không phải hàng đầu và cuối), tọa độ x ở mỗi đường sẽ là (
Bài luyện tập thêm:
- MiniPaint – SRM 178 Div 1
Note: Khi gặp một bài toán, hãy để ý xem nó có được giải trong thời gian đa thức không. Nếu có, thử xác định trạng thái của nó, cách chuyển trạng thái, và nếu không chuyển được trạng thái, hãy thử tối ưu nó về một bài QHĐ (như ví dụ ở trên).
Những bài đã đề cập ở trên:
- TCCC ’03 Semifinals 3 Div I Easy – ZigZag
- TCCC ’04 Round 4 Div I Easy – BadNeighbors
- TCCC ’04 Round 1 Div I Med – FlowerGarden
- TCO ’03 Semifinals 4 Div I Easy – AvoidRoads
- TCCC ’03 Round 4 Div I Easy – ChessMetric
- TCO ’03 Round 4 Div I Med – Jewelry
- SRM 150 Div I Med – StripePainter
- SRM 197 Div II Hard – QuickSums
- SRM 165 Div II Hard – ShortPalindromes
- SRM 208 Div I Hard – StarAdventure
- SRM 178 Div I Hard – MiniPaint