Phương pháp quy hoạch động và một số ví dụ cụ thể
Trang 1Mục lục
LỜI MỞ ĐẦU 4
CHƯƠNG 1: TỔNG QUAN VỀ QUY HOẠCH ĐỘNG 6
1.1 Bài toán tối ưu 6
1.1.1 Khái niệm………6
1.1.2 Một số ví dụ về bài toán tối ưu……… 6
1.1.3 Nguyên lý tối ưu Bellman………7
1.2 Phương pháp quy hoạch động 7
1.2.1 Phương pháp chia để trị……… 8
1.2.2 Khái niệm về quy hoạch động……….8
1.2.3 Phương pháp quy hoạch động……….10
1.2.4 Ưu điểm và hạn chế của phương pháp quy hoạch động12 CHƯƠNG 2: ỨNG DỤNG PHƯƠNG PHÁP QUY HOẠCH ĐỘNG GIẢI MỘT SỐ BÀI TOÁN CỤ THỂ 14
1 Bài toán: Chia phần thưởng 14
1.1 Phát biểu bài toán 14
1.2 Ý tưởng 30s 14
1.3 Lập hệ thức 15
1.4 Thuật toán 16
1.5 Tính đúng đắn 21
1.6 Độ phức tạp thuật toán 21
1.7 Ứng dụng thực tiễn : 21
1.8 Chương trình : 22
2 Bài toán: Lập lịch thuê thợ may hàng tháng 24
2.1 Phát biểu bài toán 24
2.2 Ý tưởng 30s 24
2.3 Lập hệ thức 24
2.4 Thuật toán 25
2.5 Tính đúng đắn 28
2.6 Độ phức tạp thuật toán 28
Trang 22.7 Ứng dụng 28
3 Bài toán: Dãy con có tổng chia hết cho k 29
3.1 Phát biểu bài toán 29
3.2 Ý tưởng sau 30s 29
3.3 Phương pháp quy hoạch động 29
3.4 Thuật giải và chương trình 30
3.5 Tính đúng đắn và tính dừng 30
3.6 Độ phức tạp thuật toán 31
3.7 Ứng dụng : 31
4 Bài toán: Tổng của n số tự nhiên sắp theo trật tự không tăng 32
4.1 Phát biểu bài toán 32
4.2 Ý tưởng 30s 32
4.3 Lập hệ thức 32
4.4 Thuật toán 33
4.5 Tính đúng đắn 37
4.6 Độ phức tạp thuật toán 37
4.7 Ứng dụng thực tiễn : 37
5 Bài toán: Khai triển nhị thức Newton 38
5.1 Phát biểu bài toán 38
5.2 Ý tưởng 30s 38
5.3 Lập hệ thức 38
5.4 Thuật toán 39
5.5 Chứng minh tính đúng đắn 40
5.6 Độ phức tạp thuật toán 40
5.7 Ứng dụng 40
6 Bài toán: Tổng số lần chuyển ghế ra và chuyển ghế vào 41
6.1 Phát biểu bài toán 41
6.2 Ý tưởng 30s 41
6.3 Lập hệ thức 42
6.4 Thuật toán 43
6.5 Tính đúng đắn 48
6.6 Độ phức tạp thuật toán 48
6.7 Ứng dụng 48
Trang 37 Bài toán: Cấp số cộng 49
7.1 Phát biểu bài toán 49
7.2 Ý tưởng 30s 49
7.3 Lập hệ thức 49
7.4 Thuật toán 50
7.5 Chứng minh tính đúng đắn 52
7.6 Độ phức tạp thuật toán 52
7.7 Ứng dụng 52
8 Bài toán: Xóa ít nhất ký tự của một xâu để được xâu đối xứng 53
8.1 Phát biểu bài toán 53
8.2 Ý tưởng 30s 53
8.3 Lập hệ thức: 54
8.4 Thuật toán 55
8.5 Tính đúng đắn 60
8.6 Độ phức tạp thuật toán 60
9 Bài toán: Người nông dân 61
9.1 Phát biểu bài toán 61
9.2 Ý tưởng 30s 61
9.3 Lập công thức 62
9.4 Thuật toán 62
9.5 Chứng minh tính đúng đắn 64
9.6 Độ phức tạp thuật toán 64
9.7 Ứng dụng 64
10 Bài toán nhân ma trận 65
10.1 Phát biểu bài toán 65
10.2 Ý tưởng 30s 66
10.3 Giải thuật quy hoạch động 66
10.4 Thuật toán 69
10.5 Tính đúng đắn và tính dừng 69
10.6 Độ phức tạp thuật toán 70
10.7 Ứng dụng của ma trận 70
Kết luận 72
Trang 4LỜI MỞ ĐẦU
Trong thực tế, khi gặp một bài toán, một vấn đề cần được giải quyết, ta luônmong muốn tìm được phương án cho kết quả tốt nhất, tối ưu nhất Bởi vậy, các lớp bàitoán về tối ưu hóa luôn đóng một cai trò quan trọng, vì tính ứng dụng cao trong thựctiễn
Đối với lớp bài toán tối ưu, có rất nhiều phương pháp để giải quyết như: phươngpháp nhánh cận, phương pháp tham lam, phương pháp quy hoạch động Tùy từng bàitoán cụ thể mà ta sử dụng phương pháp thích hợp, nhằm đạt được hiệu quả cao nhất(hiệu năng, tốc độ thực hiện, độ phức tạp, bộ nhớ sử dụng ) Trong đó, phương phápquy hoạch động (Dynamic programming) luôn được ưu tiên lựa chọn vì tính hiệu quảcao và tối ưu hơn hầu hết các phương pháp khác
Các bài toán quy hoạch động chiếm một vị trí khá quan trọng trong tổ chức hoạtđộng và sản xuất Chính vì lẽ đó mà trong các kì thi học sinh giỏi quốc gia và quốc tếchúng ta thường gặp loại toán này
Thông thường những bài toán dùng phương pháp quay lui, vét cạn cho các bàitoán quy hoạch động thì chỉ có thể vét được các tập dữ liệu nhỏ, kích thước chừng vài
Trang 5chục byte Nếu tìm được đúng hệ thức thể hiện bản chất quy hoạch động của bài toán
và khéo tổ chức dữ liệu thì ta có thể xử lí được những tập dữ liệu khá lớn
Phương pháp quy hoạch động là phương pháp giải quyết tốt các bài toán tối ưu,đồng thời khi áp dụng giải một số bài toán không tối ưu cũng cho ra kết quả rất tốt.Tuy nhiên, việc áp dụng phương pháp quy hoạch động không phải dễ, vì mỗi bài toán
có đặc điểm tính chất riêng và cách giải hoàn toàn khác nhau Vậy một bài toán nhưthế nào thì có thể áp dụng được phương pháp quy hoạch động, và liệu có phải rằng tất
cả các bài toán đều có thể giải quyết được bằng quy hoạch động?
Hy vọng trong báo cáo này, em có thể làm rõ phần nào những câu hỏi trên Vàcho mọi người thấy được tông quan về quy hoạch động và các bài toán nào giải quyếtbằng phương pháp quy hoạch động
Trang 6CHƯƠNG 1: TỔNG QUAN VỀ QUY HOẠCH ĐỘNG
1.1 Bài toán tối ưu
Bài toán tối ưu gồm có 1 hàm f gọi là hàm mục tiêu hay hàm đánh giá; các hàm
g1, g2, …, gn cho giá trị logic gọi là hàm ràng buộc Yêu cầu của bài toán là tìm mộtphương án x thoả mãn tất cả các ràng buộc g1, g2, …, gn và x là tốt nhất, theo nghĩakhông tồn tại một cấu hình x¿ nào khác thoả mãn các hàm ràng buộc mà f(x¿) tốt hơnf(x)
Bài toán tối ưu là bài toán thường có nhiều phương án chấp nhận được và mỗinghiệm có một giá trị đánh giá Mục tiêu đặt ra là tìm ra nghiệm tối ưu, đó là phương
án có giá trị đánh giá lớn nhất hoặc nhỏ nhất (tối ưu)
Ví dụ : Bài toán Ba lô: có một ba lô có thể chứa tối đa trọng lượng M và có n đồ
vật (n ≤ 100), mỗi đồ vật có trọng lượng wi và giá trị bi (M, wi, bi ϵ Z¿. Hãy chọn vàxếp các đồ vật vào ba lô để tổng giá trị của ba lô là lớn nhất
Với bài toán trên ta thấy:+ Hàm mục tiêu: ∑pi bi →max
+ Hàm ràng buộc : ∑wi pi ≤ M
Với pi =0 : đồ vật thứ I không được chọn
pi =1 : đồ vật thứ I được chọn
Tóm lại, bài toán tối ưu rất phong phú, đa dạng, được ứng dụng nhiều trong thực
tế Tuy nhiên, còn rất nhiều các bài toán tối ưu là không giải được hoặc chưa có lờigiải
Trang 71.1.3 Nguyên lý tối ưu Bellman
Nguyên lý tối ưu của R.Bellmam được phát biểu như sau: “tối ưu bước thứ nbằng cách tối ưu tất cả con đường tiến đến bước n-1 và chọn con đường có tổng chi phí
từ bước 1 đến bước n-1 và từ n-1 đến n là tốt nhất (nhiều nhất)
Nguyên lý này được thừa nhận mà không chứng minh
Nguyên lý tối ưu Bellman cũng có thể phát biểu theo một cách khác như sau:
“Với mỗi quá trình điều khiển tối ưu, bắt đầu từ trạng thái A0 Khi xây dựng trạngthái Ak bất kỳ trong quá trình đó từ trạng thái Ak-1, nếu Ak-1 là tối ưu, thì Ak xây dụngđược sẽ tối ưu Vậy trạng thái An cuối cùng sẽ là tối ưu”
Phương pháp tìm nghiệm tối ưu theo nguyên lý Bellman được gọi là phươngpháp quy hoạch động Thuật ngữ này nói lên thực chất của quá trình điều khiển tôi ưu
là động: Có thể trong một số bước đầu tiên lựa chọn điều khiển tối ưu dường nhưkhông tốt nhưng tựu chung cả quá trình lại là tốt nhất
1.2 Phương pháp quy hoạch động
Trang 81.2.1 Phương pháp chia để trị
“Chia để trị” là phương pháp tách bài toán ban đầu thành các bài toán con độclập, sau đó giải các bài toán con rồi tổ hợp dần lời giải từ bài toán con nhỏ nhất đến bàitoán ban đầu Phương pháp chia để trị là một phương pháp rất thông dụng trong tin học
và kỹ thuật tính toán
Phương pháp chia để trị thường được áp dụng cho những bài toán có bản chất đệquy: bài toán P ban đầu có thể được giải bằng lời giải của bài toán con P’ có dạnggiống như P,nhưng theo một nghĩa nào đó P’ phải nhỏ hơn P, dễ giải hơn P và việc giải
nó không cần dùng đến P Giải thuật dùng để giải
bài toán có bản chất đệ quy gọi là giải thuật đệ quy
1.2.2 Khái niệm về quy hoạch động
a Khái niệm
Phương pháp quy hoạch động do nhà toán
học người Mỹ Richard Bellman (1920- 1984) phát
minh năm 1953 Phương pháp này dùng để giải các
bài toán tối ưu có bản chất đệ qui, tức là tìm phương
án tối ưu cho bài toán đó có thể đưa về tìm phương
án tối ưu của một số hữu hạn các bài toán con Cũng
áp dụng phương pháp chia để trị, nhưng so với đệ
quy, quy hoạch động được cái tiến và tối ưu hơn rất
nhiều
Phương pháp đệ quy giải quyết bài toán theo hướng top-down (từ trên xuống),nghĩa là để giải bài toán ban đầu, ta phải đi giải các bài toán con bé hơn, và chia cácbài toán con này bé hơn nữa, đến các bài toán cơ sở Đây là một phương pháp hay, tuy
Trang 9nhiên phương pháp này sẽ gặp hạn chế về mặt thời gian, tốc độ do phải tính đi tính lạinhiều lần một số bài toán con giống nhau nào đó
Ngược lại, phương pháp quy hoạch động sử dụng nguyên lý bottom-up, nghĩa là
"đi từ dưới lên" Đầu tiên, ta sẽ phải giải các bài toán con cơ sở, đơn giản nhất, có thểtìm ngay ra nghiệm Sau đó kết hợp các bài toán con này lại để tìm lời giải cho bài toánlớn hơn và cứ như thế cho đến khi giải được bài toán ban đầu Với phương pháp này,mỗi bài toán con sau khi giải xong đều được lưu trữ lại và đem ra sử dụng nếu cần Do
đó tiết kiệm bộ nhớ và cải thiện được tốc độ
Vậy phương pháp quy hoạch động là một kỹ thuật nhằm đơn giản hóa việc tínhtoán các công thức truy hồi bằng cách lưu toàn bộ hay một phần kết quả tính toán tạimỗi bước trước đó với mục đích sử dụng lại
Quy hoạch động = Chia để trị + Hướng lưu trữ (lưu lại kết quả)
b Đặc điểm của phương pháp quy hoạch động
Khi giải một bài toán bằng cách “chia để trị”, ta chia bài toán lớn thành các bàitoán con cùng kiểu nhỏ hơn, và giải quyết bằng giải thuật đệ quy Khi đó, trên thực tế,nhiều kết quả trung gian phải tính lại nhiều lần, dẫn tới việc lãng phí và chậm tốc độ
Vì vậy, để tránh việc phải tính toán lại một số kết quả trung gian nhiều lần, tacần xây dựng 1 bảng phương án lưu giữ các kết quả đã tìm được của các bài toán con
Từ đó áp dụng cho việc tìm kiếm kết quả của các bài toán cha lớn hơn Vậy quy hoạchđộng bắt đầu từ việc giải tất cả các bài toán nhỏ nhất (bài toán cơ sở) để từ đó từngbước giải quyết những bài toán lớn hơn cho tới khi giải được bài toán lớn nhất (bàitoán ban đầu)
Trang 10Việc áp dụng bảng phương án đã khiến quy hoạch động tối uu hơn rất nhiều,giảm thiểu các quá trình tính toán, và thể hiện sức mạnh của nguyên lý chia để trị đếncao độ
Quy hoạch động thường dùng một trong 2 cách tiếp cận sau:
Tiếp cận từ dưới lên (bottom up)
Tiếp cận từ trên xuống (top down)
Cách tiếp cận từ dưới lên hiệu quả hơn nên cách tiếp cận từ dưới lên (bottom up)thường được sử dụng nhiều hơn
Tóm lại:
Quy hoạch động dùng để giải quyết bài toán tối ưu theo nguyên lý “chia để trị”nhưng cải tiến hơn nhiều phương pháp giải quyết bài toán theo hướng đệ quy, và thểhiện tư tưởng chia để trị đến cao độ
Quy hoạch động làm giảm độ phức tạp, giảm thời gian giải quyết bài
Quy hoạch động thường tiếp cận theo hướng từ dưới lên (Bottom – up)
a Bài toán thế nào thì được giải quyết bằng quy hoạch động?
Một bài toán tối ưu muốn giải được bằng phương pháp quy hoạch động khi bàitoán tối ưu đó có các đặc điểm dưới đây:
Bài toán lớn phải phân rã được thành nhiều bài toán con, mà sự phối hợp lời giảicủa các bài toán con đó cho ta lời giải của bài toán lớn
Trang 11 Vì quy hoạch động là đi giải tất cả các bài toán con nên nếu không đủ khônggian vật lý lưu trữ kết quả (bộ nhớ, đĩa …) để phối hợp chúng thì phương phápquy hoạch động cũng không thể thực hiện được
Quá trình từ bài bài toán cơ sở tìm ra lời giải bài toán ban đầu phải qua hữu hạnbước
b Các bước thược hiện quy hoạch động
Bước 1: Lập hệ thức truy hồi
Dựa vào nguyên lý tối ưu, ta chia bài toán thành từng giai đoạn, tìm cách phân
rã bài toán thành các “bài toán con” tương tự có kích thước nhỏ hơn, tìm hệ thức quan
hệ giữa kết quả bài toán kích thước đã cho với kết quả của các “bài toán con” cùngkiểu có kích thước nhỏ hơn của nó nhằm xây dựng hệ thức truy hồi
Cụ thể, ta chia việc giải bài toán thành n trạng thái: n, n-1, n-2, …, 2, 1, 0 Mỗitrạng thái k khi chịu tác động điều khiển dk sẽ biến thành trạng thái k+1 Trạng tháik+1 khi chịu tác động điều khiển dk+1 sẽ biến thành trạng thái k+2 Ta sẽ so sánh và tìmđiểm chung giữa các trạng thái d0, d1, d2, …, dk, dk+1, …, dn-1 Từ đó xây dựng hệ thứctruy hồi d là hệ thức truy hồi tổng quát cảu bài toán
Bước 2: Tổ chức dữ liệu và chương trình
Ta tổ chức dữ liệu sao cho đạt các yêu cầu sau:
Dữ liệu được tính toán dần theo các bước
Dữ liệu được lưu trữ vào bảng phương án để giảm lượng tính toán lặp lại
Kích thước bộ nhớ dành cho lưu trữ dữ liệu càng nhỏ càng tốt, kiểu dữ liệu đượcchọn phù hợp, nên chọn đơn giản dễ truy cập
Cụ thể:
Trang 12 Các giá trị của Fk thường được lưu trữ trong một bảng (mảng một chiều hoặchai, ba, v.v… chiều).
Ta tính toán và lưu lại giá trị của các bài toán cơ sở (dễ dàng tìm ra cách giải) đểlàm tiền đề giải quyết các bài toán lớn hơn
Dựa vào hệ thức truy hồi và kết quả của các bài toán cơ sở để giải các bài toánlớn hơn
Lưu trữ nghiệm tương ứng với các giá trị tối ưu trong từng gian đoạn vào bảngmột cách thích hợp
Bước 3: Truy vết, tìm nghiệm và làm tốt
Dựa vào bảng lưu trữ nghiệm và bảng phương án tối ưu trong từng giai đoạn đãxây dựng, ta sẽ tìm ra kết quả nghiệm tối ưu của bài toán
Sau khi tìm được nghiệm tối ưu của bài toán Ta tìm cách làm tốt thuật toánbằng cách thu gọn hệ thức truy hồi và giảm kích thước bộ nhớ Ví dụ như tìm cáchgiảm bộ nhớ lưu trữ từ mảng hai chiều thành mảng một chiều, từ mảng một chiềuxuống thành hệ số
Trong thực tế, khi muốn làm tốt về không gian, ta sẽ phải tổ chức lại chươngtrình và thực hiện thêm các bước làm tốt, dẫn tới thiệt hại về thời gian tính toán, vàngược lại Bởi vậy, trong một số trương hợp, ta cần điểu chỉnh giữa làm tốt không gian
và thời gian để đạt được hiệu suất àm việc tốt nhất
a Ưu điểm
Phương pháp quy hoạch động cải thiện đáng kể cả về không gian lưu trữ và thờigian tính toán so với hầu hết các phương pháp giải quyết bài toán tối ưu khác
Trang 13b Hạn chế
Việc tìm hệ thức truy hồi hoặc tìm cách phân rã bài toán không phải là dễ.Nhiều khi đòi hỏi sự phân tích tổng hợp rất công phu, dễ sai sót, khó nhận ra như thếnào là thích hợp, đòi hỏi nhiều thời gian suy nghĩ Đồng thời không phải lúc nào kếthợp lời giải của các bài toán con cũng cho kết quả của bài toán lớn hơn
Khi bảng lưu trữ đòi hỏi mảng hai, ba chiều … thì khó có thể xử lý dữ liệu khikích cỡ dữ liệu lớn Có những bài toán tối ưu không thể giải được bằng quy hoạchđộng
Tổng kết :
Không phải lúc nào việc kết hợp các bài toán con cũng cho ta kết quả của bàitoán lớn hơn Hay nói cách khác là việc tìm kiếm "công thức truy hồi" rất khó khăn.Ngoài ra, số lượng các bài toán con cần lưu trữ có thể rất lớn, không chấp nhận được vì
dữ liệu và bộ nhớ máy tính không cho phép
Trang 14CHƯƠNG 2: ỨNG DỤNG PHƯƠNG PHÁP QUY HOẠCH ĐỘNG GIẢI MỘT
SỐ BÀI TOÁN CỤ THỂ
1 Bài toán: Chia phần thưởng.
1.1.Phát biểu bài toán
Cần chia hết m phần thưởng cho n học sinh sắp theo thứ tự từ giỏi trở xuống sao cho mỗi bạn không nhận ít phần thưởng hơn bạn xếp sau mình
Gọi Chia(i, j) là số cách chia i phần thưởng cho j học sinh, ta thấy:
Nếu không có học sinh nào (j = 0) thì không có cách chia nào (Chia = 0)
Trang 15 Nếu không có phần thưởng nào (i = 0) thì chỉ có một cách chia (Chia = 1- mỗihọc sinh nhận 0 phần thưởng) Ta cũng quy ước Chia(0, 0) = 1.
Nếu số phần thưởng ít hơn số học sinh (i < j) thì trong mọi phương án chia, từhọc sinh thứ i + 1 trở đi sẽ không được nhận phần thưởng nào:
Chia(i, j) = Chia(i, i) nếu i < j
Ta xét tất cả các phương án chia trong trường hợp i ≥ j Ta tách các phương ánchia thành hai nhóm không giao nhau dựa trên số phần thưởng mà học sinh đứng cuốibảng thành tích, học sinh thứ j, được nhận:
Nhóm thứ nhất gồm các phương án trong đó học sinh thứ j không được nhậnthưởng, tức là i phần thưởng chỉ chia cho (j – 1) học sinh và do đó, số cách chia,tức là số phần tử của nhóm này sẽ là: Chia(i, j – 1)
Nhóm thứ hai gồm các phương án trong đó học sinh thứ j cũng được nhậnthưởng Khi đó, do học sinh đứng cuối bảng thành tích được nhận thưởng thìmọi học sinh khác cũng sẽ có thưởng Do ai cũng được thưởng nên ta bớt củamỗi người một phần thưởng (để họ lĩnh sau), số phần thưởng còn lại (i − j) sẽđược chia cho j học sinh Số cách chia khi đó sẽ là Chia(i − j, j)
Tổng số cách chia cho trường hợp i ≥ j sẽ là tổng số phần tử của hai nhóm, ta có:
Chia(i, j) = Chia(i, j - 1) + Chia(i - j, j).
Tổng hợp lại ta có:
Trang 16Ta có phương án đầu tiên của giải thuật Chia như sau:
Phương án 1: đệ quy, số cách chia i phần thưởng cho j học
if i < j then {0 < i < j}
Chia:=Chia(i,i) else {i >= j > 0}
Chia:=Chia(i,j-1)+Chia(i-j,j);
Trang 17Phương án này chạy chậm vì phát sinh ra quá nhiều lần gọihàm trùng lặp Bảng dưới đây liệt kê số lần gọi hàm Chia khi giải bàitoán chia thưởng với bảy phần thưởng (m = 7) và 4 học sinh (n = 4).Thí dụ, hàm Chia(1,1) sẽ được gọi 9 lần,… Tổng số lần gọi hàm Chia
là 79 79 lần gọi hàm để sinh ra kết quả 11 là quá tốn kém Ta cóphương án đầu tiên của giải thuật Chia như sau:
Làm tốt lần 1: Phương án 1 khá dễ triển khai nhưng chương trình sẽ
chạy rất lâu, bạn hãy thử gọi Chia(66,32) để trải nghiệm được điềutrên Diễn tả đệ quy thường trong sáng, nhàn tản, nhưng khi thựchiện sẽ sinh ra hiện tượng gọi lặp lại những hàm đệ quy Cải tiến đầutiên là tránh những lần gọi lặp như vậy Muốn thế chúng ta tính sẵncác giá trị của hàm theo các trị của đầu vào khác nhau và điền vàomột mảng hai chiều cc Mảng cc được mô tả như sau:
const
Trang 18MN = 70;{gioi han tren cua m va n}
type
ml1 = array[0 MN ] of longint;
ml2 = array[0 mn ] of ml1;
var cc: ml2;
Ta quy ước cc[i, j] chứa số cách chia i phần thưởng cho j học sinh
Theo phân tích của phương án 1, ta có:
cc[0, 0] = 1; cc[i, 0] = 0, với i:=1 m
cc[i, j] = cc[i, i], nếu i < j
cc[i, j] = cc[i, j - 1]+cc[i - j, j], nếu i ≥ j
Trang 19Điền bảng: Lần lượt điền theo từng cột j:= 1 n Tại mỗi cột j tađặt:
với i:=0 j-1: cc[i,j ]:= cc[i, i];
với i:= j m: cc[i, j]:= cc[i, j-1] + cc[i-j, j];
Nhận kết quả: Sau khi điền bảng, giá trị cc[m, n] chính là kếtquả cần tìm
Phương án 2: Dùng mảng 2 chiều.
cc[i,j] = số cách chia i phần thưởng cho j học sinh
function Chia2(m,n: integer):longint;
var i,j: integer;
begin
cc[0,0 ]:=1;
for i:=1 to m do cc[i,0]:=0;
for j:=1 to n do begin
for i:=0 to j-1 do cc[i,j ]:=cc[i,i ];
for i:=j to m do cc[i,j ]:=cc[i,j-1 ]+cc[i-j,j ];
end;
Chia2:=cc[m,n];
end;
Làm tốt lần 2: Dùng mảng hai chiều chúng ta chỉ có thể tính toán
được với dữ liệu nhỏ Bước cải tiến sau đây khá quan trọng: chúng tadùng mảng một chiều Quan sát kĩ quy trình gán trị cho mảng haichiều theo từng cột chúng ta dễ phát hiện ra rằng cột thứ j có thể được
Trang 20tính toán từ cột thứ j - 1 Nếu gọi c là mảng một chiều sẽ dùng, ta cho
số học sinh tăng dần bằng cách lần lượt tính j bước, với j:= 1 n Tạibước thứ j, c[i] chính là số cách chia i phần thưởng cho j học sinh.Như vậy, tại bước thứ j ta có:
c[i] tại bước j = c[i] tại bước (j – 1), nếu i < j Từ đây suy rađoạn c[0 (j – 1)] được bảo lưu
c[i] tại bước j = c[i] tại bước (j – 1) + c[i – j] tại bước j,nếu i ≥ j
Biểu thức thứ hai cho biết khi cập nhật mảng c từ bước thứ j – 1 quabước thứ j ta phải tính từ trên xuống, nghĩa là tính dần theo chiềutăng của i:= j m
Mảng c được khởi trị ở bước j = 0 như sau:
c[0] = 1; c[i] = 0, với i:= 1 m
Với ý nghĩa là, nếu có 0 học sinh thì chia 0 phần thưởng cho 0học sinh sẽ được quy định là 1 Nếu số phần thưởng m khác 0 thìchia m phần thưởng cho 0 học sinh sẽ được 0 phương án
Ta có phương án 3, dùng một mảng một chiều c như sau:
Phương án 3: dùng mảng 1 chiều c.
Tại bước thứ j, c[i ] = số cách chia i phần thưởng cho jhọc sinh
function Chia1(m,n: integer):longint;
var i,j: integer;
begin
Trang 211.6.Độ phức tạp thuật toán
động
Quy hoạchđộng cải tiến
1.7.Ứng dụng thực tiễn :
ân chia công việc trong thực tế
Lập lịch công việc, hoặc lập lịch cho máy tính…
Liệt kê một dãy tăng dần, hoặc giảm dần
1.8.Chương trình :
Code chương trình viết bằng C#:
usingSystem;
Trang 22usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
Console.WriteLine( "Nhap vao so hoc sinh:\n" );
long = long Parse(Console.ReadLine());
Console.WriteLine( "Nhap vao so phan thuong:\n" );
long m = long Parse(Console.ReadLine());
Console.WriteLine( "So cach chia m phan thuong cho n hoc sinh:\n" ); Console.WriteLine( Chia ( m , n ));
Trang 242 Bài toán: Lập lịch thuê thợ may hàng tháng
2.1 Phát biểu bài toán.
Có một hợp đồng may của đối tác bên Mỹ kéo dài trong Ttháng, người quản lý cần phải lập lịch sử dụng thợ may mọi thángcho dự án Biết rằng:
số thợ may tối thiểu cần trong tháng thứ i là Scn[i];
tiền dịch vụ khi thuê 1 thợ may mới là DV;
tiền đền bù khi sa thải một thợ may là ST;
lương tháng mỗi thợ may phải trả là LT
Cần phải thuê hay sa thải bao nhiêu thợ may mỗi tháng đểtổng chi phí thợ may của dự án là nhỏ nhất
2.3 Lập hệ thức.
Trang 25Tham số thể hiện kích thước bài toán là số tháng T
Tổng chi phí thợ may trong T tháng được tính từ tổng chi phíthợ may của
T-1 tháng cộng thêm chi phí trả thợ may của tháng thứ T
Chi phí trả thợ may của tháng thứ T bao gồm :
Tiền lương trả cho số thợ may của tháng T và
Tiền dịch vụ nếu số thợ may của tháng T lớn hơn sốthợ may tháng T-1 hay tiền sa thải nếu số thợ maytrong tháng T nhỏ hơn số thợ may của tháng T-1
Kích thước bài toán phụ thuộc vào 2 tham số: số tháng và số thợ maycủa tháng
2.4 Thuật toán.
Lập công thức đệ quy.
Scn[i] luu số công nhân cần thuê cho tháng thứ i
Smax là số công nhân của tháng cần nhiều nguời nhất
Bài toán con nhỏ nhất ứng với i = 1 (tháng dầu tiên):
C(1, j) = j * (DV + LT) với j = Scn[1] Smax
C(i, j) là chi phí tối thiểu của i tháng dầu tiên nếu tại tháng thứ
i có j công nhân duợc thuê
C(i, j) = Min{ C(i-1, k) + chi phí dể từ k nguời thành j nguời }
( i=2 T; j =Scn[i] Smax; k = Scn[i-1] Smax)
Kết quả bài toán là: Kq = Min{C(T, j) + chi phí sa thải j
nguời}
Trang 26j=Scn[T] SmaxXây dựng bài toán chứa C(i,j)
Mảng C[1 T+1, 1 Smax]: C[i, j] ghi nhận giá trị C(i, j)
Trang 27 Thuật toán tạo bảng phương án C và Truoc:
Trang 282.7 Ứng dụng
Lập lịch cho công việc
Lập lịch cho trong máy tính
Trang 293 Bài toán: Dãy con có tổng chia hết cho k
3.1 Phát biểu bài toán.
Cho dãy số gồm n số nguyên dương a1 , a2 , … , an Hãy đếm xem có baonhiêu dãy con ( các phần tử lien tiếp) có tổng chia hết cho k
INPUT:
Dòng 1: gồm 2 số nguyên dương n và k
Dòng 2: chứa n số nguyên dương
OUTPUT: ghi kết quả tìm được
3.2 Ý tưởng sau 30s
Sử dụng phương pháp vét cạn để giải bài toán Liệt kê tất cả các phương án
có thể xảy ra Chọn 2 điểm bất kì làm 2 đầu của dãy, vì có n số nên có n*(n-1)/2phương án Mỗi dãy con cần tính tổng của dãy xem có chia hết cho k hay không, độphức tạp là O(n) Như vậy độ phức tạp của ý tưởng vét cạn là O(n3)
3.3 Phương pháp quy hoạch động
5 3
1 2 3 4 5
7
Trang 30Cải tiến từ phương pháp vét: Ta thấy, muốn tính tổng 1 đoạn (i,j) bất kì ta cóthể tính toán đơn giản hơn bằng một phép tính B[j]- B[i-1] với mảng B ý nghĩa là Bi
là tổng các số từ 1 đến i Mảng B được xây dụng với công thức B[i]=B[i-1] + A[i],
độ phức tạp cho bước này là O(n)
Nhận xét về mảng B: Nếu Bi và Bj đồng dư khi chia cho k (i<j) chứng tỏrằng: Bi - Bj chia hết cho k Tức là dãy con ai+1, ai+2, …, aj có tổng chia hết cho k
Ta suy ra, cần xây dựng mảng C với Ci là số lượng số trong mảng B khi chiacho k có số dư là i (0≤ i <k) Như vậy, kết quả cần tìm là :
For (i=0; i<k; i++) C[i]=0;
For (i=0; i<=n; i++) C[B[i]%k]++;
Trang 31 Vì n xác định nên khi (i=n) thì chương trình sẽ dừng => thuật toán có tínhdừng.
Chứng minh tính đúng đắn:
Ta có Ci là số lượng số trong mảng B khi chia cho k được số dư là i, tức làtrong mảng B tồn tại Ci số chia k dư i Với Ci số ta có tất cả Ci*(Ci-1)/2 cặp số, mỗicặp gồm 2 số, tương đương với Ci*(Ci-1)/2 cặp (u,v) nào đó mà tổng các số trongđoạn (u+1,v) chia hết cho k (u<v) => thuật toán luôn đúng
3.7 Ứng dụng :
Bài toán rèn luyện tư duy, và là đề thi Olympic tin học
Trang 334 Bài toán: Tổng của n số tự nhiên sắp theo trật tự không tăng.
4.1 Phát biểu bài toán
Hãy tính số cách biểu diễn số tự nhiên m thành tổng của n số tự nhiên sắp theo trật tự không tăng Thí dụ, với m = 7, n = 4 ta có:
Gọi Bieudien(i, j) là số cách biểu diễn số tự nhiên i cho j số tự nhiên, ta thấy:
Nếu không có số tự nhiên nào (j = 0) thì không có cách biễu diễn nào(Bieudien = 0)
Nếu không có số tự nhiên nào (i = 0) thì chỉ có một cách biểu diễn (Bieudien
= 1- mỗi số tự nhiên nhận 0 số tự nhiên) Ta cũng quy ước Bieudien(0, 0) =1
Nếu số số tự nhiên ít hơn số số tự nhiên (i < j) thì trong mọi phương án , từ số
tự nhiên thứ i + 1 trở đi sẽ không được nhận số tự nhiên nào:
Trang 34Bieudien(i, j) = Bieudien(i, i) nếu i < j
Trang 35Ta xét tất cả các phương án biểu diễn trong trường hợp i ≥ j Ta tách các phương
án biểu diễn thành hai nhóm không giao nhau dựa trên số số tự nhiên mà số tự nhiênnhỏ nhất, số tự nhiên thứ j, được nhận:
Nhóm thứ nhất gồm các phương án trong đó số tự nhiên thứ j không được biểu diễn, tức là i số tự nhiên chỉ biểu diễn cho (j – 1) số tự nhiên và do đó, số cách biễu diễn, tức là số phần tử của nhóm này sẽ là: Bieudien(i, j – 1)
Nhóm thứ hai gồm các phương án trong đó số tự nhiên thứ j cũng được nhậnthưởng Khi đó, do số tự nhiên nhỏ nhất được biểu diễn thì mọi số tự nhiên kháccũng sẽ được biểu diễn Do số tự nhiên nào cũng được biểu diễn nên ta bớtmột số tự nhiên (coi như đã biểu diễn ), số số tự nhiên còn lại (i − j) sẽ đượcbiễu diễn cho j số tự nhiên Số cách biễu diễn khi đó sẽ là Bieudien(i − j, j) Tổng số cách biễu diễn cho trường hợp i ≥ j sẽ là tổng số phần tử của hai nhóm,
if i < j then {0 < i < j}
Trang 36Bieudien:=Bieudien(i,i) else {i >= j > 0}
Bieudien:=Bieudien(i,j-1)+Bieudien(i-j,j); end;
Biểu diễn với số tự nhiên (m = 7) và 4 số tự nhiên (n = 4) Thí dụ, hàmBieudien(1,1) sẽ được gọi 9 lần,… Tổng số lần gọi hàm Bieudien là 79 79 lần gọihàm để sinh ra kết quả 11 là quá tốn kém Ta có phương án đầu tiên của giải thuậtBieudien như sau:
Khá dễ triển khai nhưng chương trình sẽ chạy rất lâu, bạn hãy thử gọiBieudien(66,32) để trải nghiệm được điều trên Muốn thế chúng ta tính sẵn các giátrị của hàm theo các trị của đầu vào khác nhau và điền vào một mảng hai chiềuMang Mảng Mang được mô tả như sau:
Ta quy ước Mang[i, j] chứa số cách biễu diễn i số tự nhiên cho j số tự nhiên
Theo phân tích của phương án 1, ta có:
Mang[0, 0] = 1; Mang[i, 0] = 0, với i:=1 m
Mang[i, j] = Mang[i, i], nếu i < j
Mang[i, j] = Mang[i, j - 1]+Mang[i - j, j], nếu i ≥ j
Từ đó ta suy ra quy trình điền trị vào bảng Mang như sau:
Trang 37Khởi trị
Mang[0, 0]:= 1;
với i:=1 m: Mang[i, 0]:= 0;
Điền bảng: Lần lượt điền theo từng cột j:= 1 n Tại mỗi cột j ta đặt:với i:=0 j-1: Mang[i,j ]:= Mang[i, i];
với i:= j m: Mang[i, j]:= Mang[i, j-1] + Mang[i-j, j];
Nhận kết quả: Sau khi điền bảng, giá trị Mang[m, n] chính là kết quả cần tìm
Làm tốt 1:Dùng mảng 2 chiều
Mang[i,j] = số cách biễu diễn i số tự nhiên cho j số tự nhiên
function Bieudien2(m,n: integer):longint;
var i,j: integer;
begin
Mang[0,0 ]:=1;
for i:=1 to m do Mang[i,0]:=0;
for j:=1 to n do begin
for i:=0 to j-1 do Mang[i,j ]:=Mang[i,i ];
for i:=j to m do Mang[i,j ]:=Mang[i,j-1 ]+Mang[i-j,j ];
end;
Bieudien2:=Mang[m,n];
end;
Trang 38
Làm tốt lần 2: Dùng mảng hai chiều chúng ta chỉ có thể tính toán được với dữ liệu
nhỏ Bước cải tiến sau đây khá quan trọng: chúng ta dùng mảng một chiều Quansát kĩ quy trình gán trị cho mảng hai chiều theo từng cột chúng ta dễ phát hiện rarằng cột thứ j có thể được tính toán từ cột thứ j - 1 Nếu gọi c là mảng một chiều sẽdùng, ta cho số số tự nhiên tăng dần bằng cách lần lượt tính j bước, với j:= 1 n Tạibước thứ j, c[i] chính là số cách biễu diễn i số tự nhiên cho j số tự nhiên Như vậy,tại bước thứ j ta có:
c[i] tại bước j = c[i] tại bước (j – 1), nếu i < j Từ đây suy ra đoạn c[0 (j –1)] được bảo lưu
c[i] tại bước j = c[i] tại bước (j – 1) + c[i – j] tại bước j, nếu i ≥ j
Biểu thức thứ hai cho biết khi cập nhật mảng c từ bước thứ j – 1 qua bướcthứ j ta phải tính từ trên xuống, nghĩa là tính dần theo chiều tăng của i:= j m
Mảng c được khởi trị ở bước j = 0 như sau:
c[0] = 1; c[i] = 0, với i:= 1 m
Với ý nghĩa là, nếu có 0 số tự nhiên thì biễu diễn 0 số tự nhiên cho 0 số tựnhiên sẽ được quy định là 1 Nếu số số tự nhiên m khác 0 thì biễu diễn m số tựnhiên cho 0 số tự nhiên sẽ được 0 phương án
Ta có phương án 3, dùng một mảng một chiều c như sau:
Làm tốt lần 3: dùng mảng 1 chiều c.
Tại bước thứ j, c[i ] = số cách biễu diễn i số tự nhiên cho j số tựnhiên
function Bieudien1(m,n: integer):longint;
var i,j: integer;
begin
Trang 394.7 Ứng dụng thực tiễn :
Lập lịch công việc, hoặc lập lịch cho máy tính…
Liệt kê một dãy tăng dần, hoặc giảm dần