Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 133 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
133
Dung lượng
2,37 MB
Nội dung
Chương III KỸTHUẬTTHIẾTKẾTHUẬTTOÁN “It is not the strongest of the species that survives, nor the most intelligent that survives It is the one that is the most adaptable to change” Charles Darwin Chương giới thiệu số kỹthuật quan trọng việc tiếp cận toán tìm thuậttoán Các lớp thuậttoán thảo luận chương là: Vét cạn (exhaustive search), Chia để trị (divide and conquer), Quy hoạch động (dynamic programming) Tham lam (greedy) Các toán thực có muôn hình muôn vẻ, đưa cách thức chung để tìm giải thuật cho toán Các phương pháp “chiến lược” kinh điển Khác với thuậttoán cụ thể mà biết QuickSort, tìm kiếm nhị phân,…, vấn đề chương học theo kiểu “thuộc cài đặt”, tìm thấy thuậttoán thư viện lập trình Chúng ta khảo sát vài toán cụ thể học cách nghĩ, cách tiếp cận vấn đề, cách thiếtkế giải thuật Từ rèn luyện kỹ linh hoạt giải toán thực tế Bài Liệt kê Có số toán thực tế yêu cầu rõ: tập đối tượng cho trước có đối tượng thoả mãn điều kiện định đối tượng Bài toán gọi toán liệt kê hay toán duyệt Nếu ta biểu diễn đối tượng cần tìm dạng cấu hình biến số để giải toán liệt kê, cần phải xác định thuậttoán để theo xây dựng tất cấu hình quan tâm Có nhiều phương pháp liệt kê, chúng cần phải đáp ứng hai yêu cầu đây: Không lặp lại cấu hình Không bỏ sót cấu hình Trước nói thuậttoán liệt kê, giới thiệu số khái niệm bản: 1.1 Vài khái niệm 1.1.1 Thứ tự từ điển Nhắc lại quan hệ thứ tự toàn phần “nhỏ bằng” ký hiệu “” tập hợp 𝑆, quan hệ hai thoả mãn bốn tính chất: Với ∀ 𝑎, 𝑏, 𝑐 ∈ 𝑆 Tính phổ biến (Universality): Hoặc 𝑎 ≤ 𝑏 , 𝑏 ≤ 𝑎; Tính phản xạ (Reflexivity): 𝑎 ≤ 𝑎 Tính phản đối xứng (Antisymmetry) : Nếu 𝑎 ≤ 𝑏 𝑏 ≤ 𝑎 bắt buộc 𝑎 = 𝑏 Tính bắc cầu (Transitivity): Nếu có 𝑎 ≤ 𝑏 𝑏 ≤ 𝑐 𝑎 ≤ 𝑐 Các quan hệ ≥, >, < tự suy từ quan hệ ≤ Trên dãy hữu hạn, người ta xác định quan hệ thứ tự: Xét 𝑎1…𝑛 𝑏1…𝑛 hai dãy độ dài 𝑛, phần tử 𝑎 𝑏 có quan hệ thứ tự toàn phần “” Khi 𝑎1…𝑛 ≤ 𝑏1…𝑛 : Hoặc hai dãy giống nhau: 𝑎𝑖 = 𝑏𝑖 , ∀𝑖: ≤ 𝑖 ≤ 𝑛 Hoặc tồn số nguyên dương 𝑘 ≤ 𝑛 để 𝑎𝑘 < 𝑏𝑘 𝑎𝑖 = 𝑏𝑖 , ∀𝑖: ≤ 𝑖 < 𝑘 Thứ tự gọi thứ tự từ điển (lexicographic order) dãy độ dài 𝑛 Khi hai dãy 𝑎 𝑏 có số phần tử khác nhau, người ta xác định thứ tự từ điển Bằng cách thêm vào cuối dãy 𝑎 dãy 𝑏 phần tử đặc biệt gọi 𝜖 để độ dài 𝑎 𝑏 nhau, coi phần tử 𝜖 nhỏ tất phần tử khác, ta lại đưa xác định thứ tự từ điển hai dãy độ dài Ví dụ: (1,2,3,4) < (5,6) (𝑎, 𝑏, 𝑐) < (𝑎, 𝑏, 𝑐, 𝑑) "calculator" < "computer" Thứ tự từ điển quan hệ thứ tự toàn phần dãy 1.1.2 Chỉnh hợp, tổ hợp, hoán vị Cho 𝑆 tập hữu hạn gồm 𝑛 phần tử 𝑘 số tự nhiên Gọi 𝑋 tập số nguyên dương từ tới 𝑘: 𝑋 = {1,2, … , 𝑘} Chỉnh hợp lặp Một ánh xạ 𝑓: 𝑋 → 𝑆 cho tương ứng phần tử 𝑖 ∈ 𝑋 phần tử 𝑓(𝑖) ∈ 𝑆, gọi chỉnh hợp lặp chập 𝑘 𝑆 Do 𝑋 tập hữu hạn (𝑘 phần tử) nên ánh xạ 𝑓 xác định qua bảng giá trị (𝑓(1), 𝑓(2), … , 𝑓(𝑘)), ta đồng 𝑓 với dãy giá trị (𝑓(1), 𝑓(2), … , 𝑓(𝑘)) coi dãy giá trị chỉnh hợp lặp chập 𝑘 𝑆 Ví dụ 𝑆 = {𝐴, 𝐵, 𝐶, 𝐷, 𝐸, 𝐹} Một ánh xạ 𝑓 cho bởi: 𝑖 𝑓(𝑖) 𝐸 𝐶 𝐸 tương ứng với tập ảnh (𝐸, 𝐶, 𝐸) chỉnh hợp lặp 𝑆 Số chỉnh hợp lặp chập 𝑘 tập 𝑛 phần tử 𝑛𝑘 Chỉnh hợp không lặp Mỗi đơn ánh 𝑓: 𝑋 → 𝑆 gọi chỉnh hợp không lặp chập 𝑘 𝑆 Nói cách khác, chỉnh hợp không lặp chỉnh hợp lặp có phần tử khác đôi Ví dụ chỉnh hợp không lặp chập (𝐴, 𝐶, 𝐸) tập 𝑆 = {𝐴, 𝐵, 𝐶, 𝐷, 𝐸, 𝐹} 𝑖 𝑓(𝑖) 𝐴 𝐶 𝐸 𝑛! Số chỉnh hợp không lặp chập 𝑘 tập 𝑛 phần tử 𝑛𝑃𝑘 = (𝑛−𝑘)! Hoán vị Khi 𝑘 = 𝑛 song ánh 𝑓: 𝑋 → 𝑆 gọi hoán vị 𝑆 Nói cách khác hoán vị 𝑆 chỉnh hợp không lặp chập 𝑛 𝑆 Ví dụ: (𝐴, 𝐷, 𝐶, 𝐸, 𝐵, 𝐹) hoán vị 𝑆 = {𝐴, 𝐵, 𝐶, 𝐷, 𝐸, 𝐹} 𝑖 𝑓(𝑖) 𝐴 𝐷 𝐶 𝐸 𝐵 𝐹 Số hoán vị tập 𝑛 phần tử 𝑃𝑛 = 𝑛𝑃𝑛 = 𝑛! Tổ hợp Mỗi tập gồm 𝑘 phần tử 𝑆 gọi tổ hợp chập 𝑘 𝑆 Lấy tổ hợp chập 𝑘 𝑆, xét tất 𝑘! hoán vị nó, hoán vị chỉnh hợp không lặp chập 𝑘 𝑆 Điều tức liệt kê tất chỉnh hợp không lặp chập 𝑘 tổ hợp chập 𝑘 tính 𝑘! lần Như xét mặt số lượng: 𝑛 Số tổ hợp chập 𝑘 tập 𝑛 phần tử ( ) = 𝑘 𝑛𝑃𝑘 𝑘! 𝑛! = 𝑘!(𝑛−𝑘)! Ta có công thức khai triển nhị thức: 𝑛 𝑛 (𝑥 + 𝑎) = ∑ ( ) 𝑥 𝑘 𝑎𝑛−𝑘 𝑘 𝑛 𝑘=0 𝑛 Vì số ( ) gọi hệ số nhị thức (binomial coefficient) thứ 𝑘, bậc 𝑛 𝑘 1.2 Phương pháp sinh Phương pháp sinh áp dụng để giải toán liệt kê hai điều kiện sau thoả mãn: Có thể xác định thứ tự tập cấu hình tổ hợp cần liệt kê Từ biết cấu hình cấu hình cuối theo thứ tự Xây dựng thuậttoán từ cấu hình chưa phải cấu hình cuối, sinh cấu hình 1.2.1 Mô hình sinh Phương pháp sinh viết mô hình chung: «Xây dựng cấu hình đầu tiên»; repeat «Đưa cấu hình có»; «Từ cấu hình có sinh cấu hình còn»; until «hết cấu hình»; 1.2.2 Liệt kê dãy nhị phân độ dài 𝒏 Một dãy nhị phân độ dài 𝑛 dãy 𝑥1…𝑛 𝑥𝑖 ∈ {0,1}, ∀𝑖: ≤ 𝑖 ≤ 𝑛 Có thể nhận thấy dãy nhị phân 𝑥1…𝑛 biểu diễn nhị phân giá trị nguyên 𝑣(𝑥) (0 ≤ 𝑣(𝑥) < 2𝑛 ) Số dãy nhị phân độ dài 𝑛 2𝑛 , thứ tự từ điển dãy nhị phân độ dài 𝑛 tương đương với quan hệ thứ tự giá trị số mà chúng biểu diễn Vì vậy, liệt kê dãy nhị phân theo thứ tự từ điển nghĩa phải dãy nhị phân biểu diễn số nguyên theo thứ tự 0,1, … , 2𝑛 − Ví dụ với 𝑛 = 3, có dãy nhị phân độ dài liệt kê: 𝑥 𝑣(𝑥) 000 001 010 011 100 101 110 111 Theo thứ tự liệt kê, dãy 00 ⏟ … dãy cuối 11 ⏟ … Nếu ta có dãy 𝑛 𝑛 nhị phân độ dài 𝑛, ta sinh dãy nhị phân cách cộng thêm (theo số có nhớ) vào dãy 10101111 + ──────── 10110000 Dựa vào tính chất phép cộng hai số nhị phân, cấu hình sinh từ cấu hình cách: xét từ cuối dãy lên đầu dã y (xet từ hàng đơn vị lên), tìm số gặp đầu tiên… Nếu thấy thay số số đặt tất phần tử phía sau vị trí Nếu không thấy thì toàn dãy số 1, cấu hình cuối Input Số nguyên dương 𝑛 Output Các dãy nhị phân độ dài 𝑛 Sample Input Sample Output 000 001 010 011 100 101 110 111 BINARYSTRINGS_GEN.PAS Thuậttoán sinh liệt kê dãy nhị phân {$MODE OBJFPC} program BinaryStringEnumeration; var x: AnsiString; n, i: Integer; begin ReadLn(n); SetLength(x, n); FillChar(x[1], n, '0'); //Cấu hình ban đầu x=00 repeat WriteLn(x); //Tìm số từ cuối dãy i := n; while (i > 0) and (x[i] = '1') Dec(i); if i > then //Nếu tìm thấy begin x[i] := '1'; //Thay x[i] bằng số if i < n then //Đặt x[i+1 n] := FillChar(x[i + 1], n - i, '0'); end else Break; //Không tìm thấy số nào dãy dừng until False; end 1.2.3 Liệt kê tập có 𝒌 phần tử Ta lập chương trình liệt kê tập 𝑘 phần tử tập 𝑆 = {1,2, … , 𝑛} theo thứ tự từ điẻ n Ví dụ: 𝑛 = 5, 𝑘 = 3, có 10 tập con: {1, 2, 3} {1, 2, 4} {1, 2, 5} {1, 3, 4} {1, 3, 5} {1, 4, 5} {2, 3, 4} {2, 3, 5} {2, 4, 5} {3, 4, 5} Bài toán liệt kê tập 𝑘 phần tử tập 𝑆 = {1,2, … , 𝑛} quy toán liệt kê dãy 𝑘 phần tử 𝑥1…𝑘 , ≤ 𝑥1 < 𝑥2 < ⋯ < 𝑥𝑘 ≤ 𝑛 Nếu xếp dãy theo thứ tự từ điển, ta nhận thấy: Tập (cấu hình khởi tạo) {1,2, … , 𝑘} Tạ p cuó i cù ng (cấu hình kết thúc) {𝑛 − 𝑘 + 1, 𝑛 − 𝑘 + 2, … , 𝑛} Xet mọ t tạ p {𝑥1…𝑘 } ≤ 𝑥1 < 𝑥2 < ⋯ < 𝑥𝑘 ≤ 𝑛, ta có nhận xét giới hạn (giá trị lớn nhận) 𝑥𝑘 n, 𝑥𝑘−1 𝑛 − 1, 𝑥𝑘−2 𝑛 − 2… Tổng quát: giới hạn 𝑥𝑖 là 𝑛 − 𝑘 + 𝑖 Còn tất nhiên, giới hạn (gia trị nhỏ nhá t co thẻ nhạ n) 𝑥𝑖 là 𝑥𝑖−1 + Từ mọ t dã y 𝑥1…𝑘 đạ i diẹ n cho mọ t tạ p củ a S, né u tất phần tử x đạt tới giới hạn tren thì x là cá u hình cuó i cù ng, không ta phải sinh dãy tăng dần thoả mã n: dã y mơi vừa đủ lớn dãy cũ theo nghĩa dã y k phần tử chen chúng thứ tự từ điển Ví dụ: 𝑛 = 9, 𝑘 = Cấu hình có 𝑥 = (1,2, 6,7,8,9) Các phần tử 𝑥3…6 đạt tới giới hạn trên, nên để sinh cấu hình ta sinh cách tăng phần tử số cac phà n tử 𝑥3…6 lên được, ta phải tăng 𝑥2 = lên đơn vị thà nh 𝑥2 = Được cấu hình 𝑥 = (1,3,6,7,8,9) Cấu hình lớn cấu hình trước chưa thoả mãn tính chất vừa đủ lớn Muốn tìm cá u hình vừa đủ lơn cá u hình cũ , cà n co them thao tac: Thay cac gia trị 𝑥3…6 giới hạn chung Tức là: 𝑥3 ≔ 𝑥2 + = 𝑥4 ≔ 𝑥3 + = 𝑥5 ≔ 𝑥4 + = 𝑥6 ≔ 𝑥5 + = Ta cấu hình 𝑥 = (1,3,4,5,6,7) cấu hình Tié p tụ c vơi cá u hình nà y, ta lại nhận thấy 𝑥6 = chưa đạt giới hạn trên, cần tăng 𝑥6 lên cá u hình mơi 𝑥 = (1,3,4,5,6,8) Thuậttoan sinh dã y từ dã y co 𝑥1…𝑘 xây dựng sau: Tìm từ cuối dãy lên đầu gặp phần tử 𝑥𝑖 chưa đạt giới hạn 𝑛 − 𝑘 + 𝑖… Nếu tìm thấy: Tăng 𝑥𝑖 lên Đặt tất phần tử 𝑥𝑖+1…𝑘 giới hạn củ a chung Nếu không tìm thấy tức phần tử đạt giới hạn trên, cấu hình cuối Input Hai số nguyên dương 𝑛, 𝑘, (1 ≤ 𝑘 ≤ 𝑛 ≤ 100) Output Các tập k phần tử tập {1,2, … , 𝑛} Sample Input Sample {1, 2, {1, 2, {1, 2, {1, 3, {1, 3, {1, 4, {2, 3, {2, 3, {2, 4, {3, 4, Output 3} 4} 5} 4} 5} 5} 4} 5} 5} 5} SUBSETS_GEN.PAS Thuậttoán sinh liệt kê tập 𝑘 phần tử {$MODE OBJFPC} program SubSetEnumeration; const max = 100; var x: array[1 max] of Integer; n, k, i, j: Integer; begin ReadLn(n, k); for i := to k x[i] := i; //Khởi tạo x := (1, 2, , k)} repeat //In cấu hình tại Write('{'); for i := to k begin Write(x[i]); if i < k then Write(', '); end; WriteLn('}'); //Duyệt từ cuối dãy lên tìm x[i] chưa đạt giới hạn n – k + i i := k; while (i > 0) and (x[i] = n - k + i) Dec(i); if i > then //Nếu tìm thấy begin Inc(x[i]); //Tăng x[i] lên //Đặt x[i + k] bằng giới hạn chúng for j := i + to k x[j] := x[j - 1] + 1; end else Break; until False; end 1.2.4 Liệt kê hoán vị Ta lập chương trình liệt kê hoán vị tập 𝑆 = {1,2, … , 𝑛} theo thứ tự từ điển Ví dụ với n = 3, có hoán vị: (1,2,3); (1,3,2); (2,1,3); (2,3,1); (3,1,2); (3,2,1) Mỗi hoán vị tập 𝑆 = {1,2, … , 𝑛} biểu diễn dạng một dãy số 𝑥1…𝑛 Theo thứ tự từ điển, ta nhận thấy: Hoán vị cần liệt kê: (1,2, … , 𝑛) Hoán vị cuối cần liệt kê: (𝑛, 𝑛 − 1, … ,1) Bắt đầu từ hoán vị (1,2, … , 𝑛), ta sinh hoán vị lại theo quy tắc: Hoán vị sinh phải hoán vị vừa đủ lớn hoán vị theo nghĩa có hoán vị khác chen chúng thứ tự Giả sử hoán vị 𝑥 = (3,2,6,5,4,1), xét phần tử cuối cùng, ta thấy chúng xếp giảm dần, điều có nghĩa cho dù ta có hoán vị phần tử nào, ta hoán vị bé hoán vị Như ta phải xét đến 𝑥2 = thay giá trị khác Ta thay giá trị nào?, hoán vị nhỏ hơn, có 𝑥1 = (phần tử sau không chọn vào giá trị mà phần tử trước chọn) Còn lại giá trị: 4, Vì cần hoán vị vừa đủ lớn nên ta chọn 𝑥2 ≔ Còn giá trị 𝑥3…6 lấy tập {2,6,5,1} Cũng tính vừa đủ lớn nên ta tìm biểu diễn nhỏ số gán cho 𝑥3…6 tức 𝑥3…6 ≔ (1,2,5,6) Vậy hoán vị (3,4,1,2,5,6) Ta có nhận xét qua ví dụ này: Đoạn cuối hoán vị 𝑥3…6 xếp giảm dần, số 𝑥5 = số nhỏ đoạn cuối giảm dần thoả mãn điều kiện lớn 𝑥2 = Nếu đảo giá trị 𝑥5 𝑥2 ta hoán vị (3,4,6,5,2,1), đoạn cuối 𝑥3…6 xếp giảm dần Khi muốn biểu diễn nhỏ cho giá trị đoạn cuối ta cần đảo ngược đoạn cuối Trong trường hợp hoán vị (2,1,3,4) hoán vị (2,1,4,3) Ta coi hoán vị (2,1,3,4) có đoạn cuối giảm dần, đoạn cuối gồm phần tử (4) Thuậttoán sinh hoán vị từ hoán vị xây dựng sau: Xác định đoạn cuối giảm dần dài nhất, tìm số 𝑖 phần tử 𝑥𝑖 đứng liền trước đoạn cuối Điều đồng nghĩa với việc tìm từ vị trí sát cuối dãy lên đầu, gặp số 𝑖 thỏa mãn 𝑥𝑖 < 𝑥𝑖+1 Nếu tìm thấy số 𝑖 Trong đoạn cuối giảm dần, tìm phần tử 𝑥𝑘 nhỏ vừa đủ lớn 𝑥𝑖 Do đoạn cuối giảm dần, điều thực cách tìm từ cuối dãy lên đầu gặp số 𝑘 thoả mãn 𝑥𝑘 > 𝑥𝑖 (có thể dùng tìm kiếm nhị phân) Đảo giá trị 𝑥𝑘 𝑥𝑖 Lật ngược thứ tự đoạn cuối giảm dần (𝑥𝑖+1…𝑛 ), đoạn cuối trở thành tăng dần Nếu không tìm thấy tức toàn dãy giảm dần, cấu hình cuối Input Số nguyên dương 𝑛 ≤ 100 Output Các hoán vị dãy (1,2, … , 𝑛) Sample Input Sample (1, 2, (1, 3, (2, 1, (2, 3, (3, 1, (3, 2, Output 3) 2) 3) 1) 2) 1) PERMUTATIONS_GEN.PAS Thuậttoán sinh liệt kê hoán vị {$MODE OBJFPC} program PermutationEnumeration; const max = 100; var x: array[1 max] of Integer; n, i, k, l, h: Integer; //Thủ tục đảo giá trị hai tham biến x, y procedure Swap(var x, y: Integer); var temp: Integer; begin temp := x; x := y; y := temp; end; begin ReadLn(n); for i := to n x[i] := i; repeat //In cấu hình tại Write('('); for i := to n begin Write(x[i]); if i < n then Write(', '); end; WriteLn(')'); //Sinh cấu hình kế tiếp //Tìm i số đứng trước đoạn cuối giảm dần i := n - 1; while (i > 0) and (x[i] > x[i + 1]) Dec(i); if i > then //Nếu tìm thấy begin //Tìm từ cuối dãy phần tử (x[k]) lớn x[i] k := n; while x[k] < x[i] Dec(k); //Đảo giá trị x[k] x[i] Swap(x[k], x[i]); //Lật ngược thứ tự đoạn cuối giảm dần, đoạn cuối trở thành tăng dần l := i + 1; h := n; while l < h begin Swap(x[l], x[h]); Inc(l); Dec(h); end; end else Break; //Cả dãy giảm dần, hết cấu hình until False; end Nhược điểm phương pháp sinh sinh cấu hình thứ 𝑝 chưa có cấu hình thứ 𝑝 − 1, điều làm phương pháp sinh tính phổ dụng thuậttoán duyệt hạn chế Hơn nữa, cấu hình ban đầu lúc dễ tìm được, kỹthuật sinh cấu hình cho toán đơn giản (Sinh chỉnh hợp không lặp chập 𝑘 theo thứ tự từ điển chẳng hạn) Ta sang chuyên mục sau nói đến phương pháp liệt kê có tính phổ dụng cao hơn, để giải toán liệt kê phức tạp là: Thuậttoán quay lui (Back tracking) 1.3 Thuậttoán quay lui Thuậttoán quay lui dùng để giải toán liệt kê cấu hình Thuậttoán làm việc theo cách: Mỗi cấu hình xây dựng cách xây dựng phần tử Mỗi phần tử chọn cách thử tất khả Giả sử cấu hình cần liệt kê có dạng 𝑥1…𝑛 , thuậttoán quay lui xét tất giá trị 𝑥1 nhận, thử cho 𝑥1 nhận giá trị Với giá trị thử gán cho 𝑥1 , thuậttoán xét tất giá trị 𝑥2 nhận, lại thử cho 𝑥2 nhận giá trị Với giá trị thử gán cho 𝑥2 lại xét tiếp khả chọn 𝑥3 , tiếp tục vậy… Mỗi ta tìm đầy đủ cấu hình liệt kê cấu hình Có thể mô tả thuậttoán quay lui theo cách quy nạp: Thuậttoán liệt kê cấu hình 𝑛 𝑓(𝑥)𝑑𝑇 (𝑥) + 𝑓(𝑦)𝑑𝑇 (𝑦) = (𝑓(𝑥) + 𝑓(𝑦))(𝑑𝑇′ (𝑧) + 1) = 𝑓(𝑧)𝑑𝑇 ′ (𝑧) + (𝑓(𝑥) + 𝑓(𝑦)) (4.3) Từ suy 𝐵(𝑇) = 𝐵(𝑇 ′ ) + 𝑓(𝑥) + 𝑓(𝑦) hay 𝐵(𝑇 ′ ) = 𝐵(𝑇) − 𝑓(𝑥) − 𝑓(𝑦) Giả sử phản chứng 𝑇 không tối ưu, gọi 𝑇̂ tối ưu tập ký tự 𝐶 Xét 𝑇̂, không giảm tính tổng quát, coi 𝑥, 𝑦 anh-em (Bổ đề 4-4) Đưa ký tự 𝑧 vào nút cha chung 𝑥 𝑦 với tần suất 𝑓(𝑧) ≔ 𝑓(𝑥) + 𝑓(𝑦) cắt bỏ hai nút 𝑥, 𝑦 𝑇̂ Ta 𝑇̂′ tương ứng với mã phi tiền tố 𝐶′ Trong đó: 𝐵(𝑇̂ ′ ) = 𝐵(𝑇̂) − 𝑓(𝑥) − 𝑓(𝑦) < 𝐵(𝑇) − 𝑓(𝑥) − 𝑓(𝑦) (do T khong tó i ưu) = 𝐵(𝑇 ′ ) (4.4) Vậy 𝐵(𝑇̂ ′ ) < 𝐵(𝑇 ′ ), mâu thuẫn với giả thiết 𝑇′ tối ưu tập ký tự 𝐶′ Ta có điều phải chứng minh Tính đắn thuậttoán Huffman suy trực tiếp từ Định lý 4-5: Để tìm tối ưu tập ký tự 𝐶, ta tìm hai ký tự có tần suất thấp 𝑥, 𝑦 nhập chúng lại thành ký tự 𝑧 Sau thiết lập quan hệ cha-con 𝑧 𝑥, 𝑦, ta quy toán con: Tìm tối ưu tập 𝐶 ′ ≔ 𝐶 − {𝑥, 𝑦} ∪ {𝑧} có lực lượng 𝐶 phần tử Cài đặt Chúng ta cài đặt chương trình tìm mã phi tiền tố tối ưu để mã hóa ký tự xâu ký tự đầu vào 𝑆 Input Xâu ký tự 𝑆 Output Các từ mã tương ứng với ký tự Sample Input abcdefabcdefabcdeabcdabdadaaaaaaaa Sample Output = a 100 = c 101 = b 1100 = f 1101 = e 111 = d 30 14 20 a 11 c b f e d Hàng đợi ưu tiên nút chương trình tổ chức dạng Binary Heap Trên ta cài đặt thao tác 𝐻𝑒𝑎𝑝𝑖𝑓𝑦(𝑟) để vun nhánh Heap gốc 𝑟 thành đống điều kiện hai nhánh (gốc 2𝑟 2𝑟 + 1) đống Tại bước thuậttoán Huffman, thay cài đặt chế “ra hai vào một”, ta sử dụng mẹo nhỏ: Lấy khỏi Heap (𝑃𝑜𝑝) nút 𝑥, đọc nút 𝑦 gốc Heap (𝐺𝑒𝑡), tạo nút 𝑧 cha chung 𝑥 𝑦, đưa nút 𝑧 vào gốc Heap đè lên nút 𝑦 thực vun đống từ gốc: 𝐻𝑒𝑎𝑝𝑖𝑓𝑦(1) HUFFMAN.PAS Mã hóa Huffman {$MODE OBJFPC} program HuffmanCode; const MaxLeaves = 256; //Các ký tự kiểu AnsiChar, cần đổi kích thước nếu dùng kiểu liệu khác type TNode = record //Cấu trúc nút Huffman f: Integer; c: AnsiChar; l, r: Pointer; end; PNode = ^TNode; //Kiểu trỏ tới nút THeap = record //Cấu trúc liệu Binary Heap items: array[1 MaxLeaves] of PNode; nItems: Integer; end; var s: AnsiString; //Xâu ký tự đầu vào Heap: THeap; //Hàng đợi ưu tiên TreeRoot: PNode; //Gốc Huffman bits: array[1 MaxLeaves] of Integer; //Tạo nút chứa tần suất fq, ký tự ch, hai nhánh left, right function NewNode(fq: Integer; ch: AnsiChar; left, right: PNode): PNode; begin New(Result); with Result^ begin f:= fq; c := ch; l := left; r := right; end; end; //Định nghĩa quan hệ ưu tiên là quan hệ nItems) or not (items[c]^ < temp^) then Break; items[r] := items[c]; r := c; until False; items[r] := temp; end; end; //Đọc nút ở gốc Heap function Get: PNode; begin with Heap Result := items[1]; end; //Lấy nút có tần suất nhỏ khỏi Heap function Pop: PNode; begin with Heap begin Result := items[1]; items[1] := items[nItems]; Dec(nItems); Heapify(1); end; end; //Thay nút ở gốc Heap bởi node procedure UpdateRootHeap(node: PNode); begin with Heap begin items[1] := node; Heapify(1); end; end; procedure InitHeap; //Khởi tạo Heap ban đầu chứa nút var c: AnsiChar; i: Integer; freq: array[AnsiChar] of Integer; begin FillDWord(freq, Length(freq), 0); for i := to Length(s) Inc(freq[s[i]]); //Đếm tần suất with Heap begin nItems := 0; for c := Low(AnsiChar) to High(AnsiChar) if freq[c] > then //Xét ký tự S begin Inc(nItems); items[nItems] := NewNode(freq[c], c, nil, nil); //Tạo nút chứa ký tự end; for i := nItems div downto //Vun đống từ lên Heapify(i); end; end; procedure BuildTree; //Thuật toán Huffman var x, y, z: PNode; begin while Heap.nItems > //Chừng Heap nhiều phần tử begin x := Pop; //Lấy nút tần suất nhỏ khỏi Heap y := Get; //Đọc nút tần suất nhỏ tiếp theo ở gốc Heap z := NewNode(x^.f + y^.f, #0, x, y); //Tạo nút z cha x y UpdateRootHeap(z); //Đưa z vào gốc Heap thực vun đống: Ra x y, vào z end; TreeRoot := Heap.items[1]; //Giữ lại gốc Huffman end; //Thủ tục in từ mã Huffman gốc node, depth là độ sâu node procedure Traversal(node: PNode; depth: Integer); //Duyệt Huffman gốc node var i: Integer; begin if node^.l = nil then //Nếu node nút begin //In dãy bit biểu diễn ký tự for i := to depth Write(bits[i]); WriteLn(' = ', node^.c); Dispose(node); end else //Nếu node nút nhánh begin bits[depth + 1] := 0; //Các từ mã nhánh trái có bit tiếp theo Traversal(node^.l, depth + 1); //Duyệt nhánh trái bits[depth + 1] := 1; //Các từ mã nhánh phải có bit tiếp theo Traversal(node^.r, depth + 1); //Duyệt nhánh phải end; end; begin ReadLn(s); InitHeap; BuildTree; Traversal(TreeRoot, 0); end Xét riêng thuậttoán Huffman thủ tục BuildTree Ta thấy 𝑛 số ký tự (số nút Huffman) vòng lặp while lặp 𝑛 − lần Các lời gọi thủ tục bên vòng lặp có thời gian thực Ο(lg 𝑛) Vậy thuậttoán Huffman có thời gian thực Ο(𝑛 lg 𝑛) Chúng ta khảo sát vài toán mà đó, thuậttoán tham lam thiếtkế dựa mô hình chia để trị Thuậttoán tham lam cần đưa định tức bước lựa chọn Quyết định ảnh hưởng tới lựa chọn bước Chính vậy, phân tích kỹ hai định liên tiếp cho ta tính chất nghiệm tối ưu từ xây dựng thuậttoán hiệu 4.3.4 Lịch xử lý lỗi Bạn người quản trị hệ thống thông tin, hệ thống lúc xảy 𝑛 lỗi đánh số từ tới 𝑛 Lỗi 𝑖 gây thiệt hại 𝑑𝑖 sau ngày để khắc phục lỗi cần 𝑡𝑖 ngày Tại thời điểm, bạn xử lý lỗi Hãy lên lịch trình xử lý lỗi cho tổng thiệt hại 𝑛 lỗi nhỏ Input Dòng chứa số nguyên dương 𝑛 ≤ 105 𝑛 dòng tiếp theo, dòng chứa hai số nguyên dương 𝑑𝑖 , 𝑡𝑖 ≤ 100 Output Lịch xử lý lỗi Sample Input 3 Sample Output Bug 4: fixed time = Bug 2: fixed time = Bug 3: fixed time = Bug 1: fixed time = Minimum Damage = 44 1; 3; 6; 9; damage damage damage damage = = = = 24 2 Do lỗi ngưng gây thiệt hại khắc phục, nên dễ thấy lịch trình cần tìm phải có tính chất: Khi bắt tay vào xử lý lỗi, ta phải làm liên tục lỗi khắc phục Tức ta cần tìm hoán vị dãy số (1,2, … , 𝑛) tương ứng với thứ tự lịch trình khắc phục lỗi Giả sử lịch trình 𝐴 = (1,2, … 𝑖, 𝑖 + 1, … , 𝑛) xử lý lỗi từ tới 𝑛 lịch trình tối ưu Ta lấy lịch trình khác 𝐵 = (1,2, … , 𝑖 + 1, 𝑖, … , 𝑛) có cách đảo thứ tự xử lý hai lỗi liên tiếp: 𝑖 𝑖 + lịch trình 𝐴 Ta nhận xét hai lỗi 𝑖 𝑖 + 1, thời điểm lỗi khác khắc phục giống hai lịch trình, có nghĩa khác biệt tổng thiệt hại lịch trình 𝐴 lịch trình 𝐵 nằm thiệt hại hai lỗi 𝑖 𝑖 + gây Gọi 𝑇 thời điểm lỗi 𝑖 − khắc phục (trên hai lịch trình 𝐴 𝐵) Khi đó: Nếu theo lịch trình 𝐴 = (1,2, … 𝑖, 𝑖 + 1, … , 𝑛), thiệt hại hai lỗi 𝑖 𝑖 + gây là: 𝛼 = (𝑇 + 𝑡𝑖 )𝑑𝑖 + (𝑇 + 𝑡𝑖 + 𝑡𝑖+1 )𝑑𝑖+1 (4.5) Nếu theo lịch trình 𝐵 = (1,2, … , 𝑖 + 1, 𝑖, … , 𝑛), thiệt hại hai lỗi 𝑖 𝑖 + gây là: 𝛽 = (𝑇 + 𝑡𝑖+1 + 𝑡𝑖 )𝑑𝑖 + (𝑇 + 𝑡𝑖+1 )𝑑𝑖+1 (4.6) Thiệt hại theo lịch trình 𝐴 lớn thiệt hại theo lịch trình 𝐵 (do 𝐴 tối ưu), tức thiệt hại 𝛼 không lớn thiệt hại 𝛽 Loại bỏ hạng tử giống công thức tính 𝛼 𝛽, ta có: 𝛼 ≤ 𝛽 ⇔ 𝑡𝑖 𝑑𝑖+1 ≤ 𝑡𝑖+1 𝑑𝑖 𝑡𝑖 𝑡𝑖+1 𝛼≤𝛽⟺ ≤ 𝑑𝑖 𝑑𝑖+1 (4.7) Vậy lịch trình 𝐴 = (1,2, … , 𝑛) tối ưu, phải thỏa mãn: 𝑡1 𝑡2 𝑡𝑛 ≤ ≤⋯≤ 𝑑1 𝑑2 𝑑𝑛 𝑡 𝑡 Ngoài thấy 𝑑𝑖 = 𝑑𝑖+1 𝛼 = 𝛽, hai lịch trình sửa chữa 𝐴, 𝐵 có mức độ 𝑖 𝑖+1 thiệt hại Trong trường hợp này, việc sửa lỗi 𝑖 trước hay 𝑖 + trước cho phương án tối ưu Từ suy phương án tối ưu cần tìm phương án khắc phục lỗi theo thứ 𝑡(.) tự tăng dần tỉ số 𝑑 Lời giải lặp đơn thuậttoán xếp (.) BUGFIXSCHEDULING.PAS Lịch xử lý lỗi {$MODE OBJFPC} program BugFixes; const max = 100000; type TBug = record d, t: Integer; end; var bugs: array[1 max] of TBug; id: array[1 max] of Integer; n: Integer; procedure Enter; //Nhập liệu var i: Integer; begin ReadLn(n); for i := to n with bugs[i] begin ReadLn(d, t); id[i] := i; end; end; //Định nghĩa lại toán tử < lỗi Lỗi x gọi là “nhỏ hơn” lỗi y nếu x.t/x.d < y.t/y.d operator < (const x, y: TBug): Boolean; begin Result := x.t * y.d < y.t * x.d; end; //Sắp xếp lỗi bugs[L H] theo quan hệ thứ tự “nhỏ hơn” định nghĩa ở procedure QuickSort(L, H: Integer); var i, j: Integer; pivot: Integer; begin if L >= H then Exit; i := L + Random(H - L + 1); pivot := id[i]; id[i] := id[L]; i := L; j := H; repeat while (bugs[pivot] < bugs[id[j]]) and (i < j) Dec(j); if i < j then begin id[i] := id[j]; Inc(i); end else Break; while (bugs[id[i]] < bugs[pivot]) and (i < j) Inc(i); if i < j then begin id[j] := id[i]; Dec(j); end else Break; until i = j; id[i] := pivot; QuickSort(L, i - 1); QuickSort(i + 1, H); end; procedure PrintResult; //In kết var i: Integer; Time, Damage: Integer; TotalDamage: Int64; begin Time := 0; TotalDamage := 0; for i := to n with bugs[id[i]] begin Time := Time + t; Damage := Time * d; WriteLn('Bug ', id[i], ': fixed time = ', Time, '; damage = ', Damage); Inc(TotalDamage, Damage); end; WriteLn('Minimum Damage = ', TotalDamage); end; begin Enter; QuickSort(1, n); PrintResult; end 4.3.5 Thuậttoán Johnson Bài toán Bài toán lập lịch gia công hai máy (Two-machine flow shop model): Có 𝑛 chi tiết đánh số từ tới 𝑛 hai máy 𝐴, 𝐵 Một chi tiết cần gia công máy 𝐴 trước chuyển sang gia công máy 𝐵 Thời gian gia công chi tiết 𝑖 máy 𝐴 𝐵 𝑎𝑖 𝑏𝑖 Tại thời điểm, máy gia công chi tiết Hãy lập lịch gia công chi tiết cho việc gia công toàn 𝑛 chi tiết hoàn thành thời gian sớm Input Dòng chứa số nguyên dương 𝑛 ≤ 105 Dòng chứa 𝑛 số nguyên dương 𝑎1 , 𝑎2 , … , 𝑎𝑛 (∀𝑖: 𝑎𝑖 ≤ 100) Dòng chứa 𝑛 số nguyên dương 𝑏1 , 𝑏2 , … , 𝑏𝑛 (∀𝑖: 𝑏𝑖 ≤ 100) Output Lịch gia công tối ưu Sample Input 4 Sample Job 2: Job 1: Job 4: Job 3: Time = Output A[0, 1]; B[1, 3] A[1, 4]; B[4, 8] A[4, 8]; B[8, 11] A[8, 10]; B[11, 12] 12 10 11 12 2 4 Thuậttoán Nhận xét tồn lịch gia công tối ưu mà chi tiết gia công máy 𝐴 theo thứ tự gia công máy 𝐵 theo thứ tự Máy 𝐴 hoạt động liên tục không nghỉ máy 𝐵 có khoảng thời gian chết chờ chi tiết từ máy 𝐴 Như ta cần tìm lịch gia công tối ưu dạng hoán vị dãy số (1,2, … , 𝑛) Định lý 4-6 (Định lý Johnson) Một lịch gia công tối ưu cần thỏa mãn tính chất: Nếu chi tiết 𝑖 gia công trước chi tiết 𝑗 min(𝑎𝑖 , 𝑏𝑗 ) ≤ min(𝑎𝑗 , 𝑏𝑖 ) Hơn min(𝑎𝑖 , 𝑏𝑗 ) = min(𝑎𝑗 , 𝑏𝑖 ) việc đảo thứ tự gia công chi tiết 𝑖 chi tiết 𝑗 phương án tối ưu trì tính tối ưu phương án Định lý 4-7 Xét quan hệ hai “nhỏ bằng” (ký hiệu ≼) xác định tập chi tiết Quan hệ định nghĩa sau: 𝑖 ≼ 𝑗 nếu: Hoặc min(𝑎𝑖 , 𝑏𝑗 ) < min(𝑎𝑗 , 𝑏𝑖 ) Hoặc min(𝑎𝑖 , 𝑏𝑗 ) = min(𝑎𝑗 , 𝑏𝑖 ) 𝑖 ≤ 𝑗 Khi quan hệ ≼ quan hệ thứ tự toàn phần (có đầy đủ tính chất: phổ biến, phản xạ, phản đối xứng, bắc cầu) Việc chứng minh cụ thể hai định lý dài dòng, bạn tham khảo tài liệu khác Cài đặt Định lý 4-6 (Định lý Johnson) Định lý 4-7 thuậttoán Johnson cài đặt thuậttoán xếp so sánh Tuy nhiên cài đặt thuậttoán xếp so sánh, cần cài đặt phép toán 𝑖 ≺ 𝑗: cho biết chi tiết 𝑖 bắt buộc phải gia công trước chi tiết 𝑗 việt kiểm tra bất đẳng thức min(𝑎𝑖 , 𝑏𝑗 ) < min(𝑎𝑗 , 𝑏𝑖 ) Bởi min(𝑎𝑖 , 𝑏𝑗 ) = min(𝑎𝑗 , 𝑏𝑖 ), gia công chi tiết 𝑖 trước hay 𝑗 trước JOHNSONALG.PAS Thuậttoán Johnson {$MODE OBJFPC} program JohnsonAlgorithm; uses Math; const maxN = 100000; type TJob = record a, b: Integer; end; var jobs: array[1 maxN] of TJob; id: array[1 maxN] of Integer; n: Integer; procedure Enter; //Nhập liệu var i: Integer; begin ReadLn(n); for i := to n Read(jobs[i].a); ReadLn; for i := to n Read(jobs[i].b); for i := to n id[i] := i; end; //Định nghĩa toán tử "phải làm trước": < Chi tiết x phải làm trước chi tiết y nếu Min(x.a, y.b) < Min(y.a, x.b) operator < (const x, y: TJob): Boolean; //Toán tử dùng cho xếp begin Result := Min(x.a, y.b) < Min(y.a, x.b); end; //Thuật toán QuickSort xếp bằng số procedure QuickSort(L, H: Integer); var i, j: Integer; pivot: Integer; begin if L >= H then Exit; i := L + Random(H - L + 1); pivot := id[i]; id[i] := id[L]; i := L; j := H; repeat while (jobs[pivot] < jobs[id[j]]) and (i < j) Dec(j); if i < j then begin id[i] := id[j]; Inc(i); end else Break; while (jobs[id[i]] < jobs[pivot]) and (i < j) Inc(i); if i < j then begin id[j] := id[i]; Dec(j); end; else Break; until i = j; id[i] := pivot; QuickSort(L, i - 1); QuickSort(i + 1, H); end; procedure PrintResult; //In kết var StartA, StartB, FinishA, FinishB: Integer; i, j: Integer; begin StartA := 0; StartB := 0; for i := to n //Duyệt danh sách đã xếp begin j := id[i]; FinishA := StartA + jobs[j].a; StartB := Max(StartB, FinishA); FinishB := StartB + jobs[j].b; WriteLn('Job ', j, ': ', 'A[', StartA, ', ', FinishA, ']; B[', StartB, ', ', FinishB, ']'); StartA := FinishA; StartB := FinishB; end; WriteLn('Time = ', FinishB); end; begin Enter; QuickSort(1, n); PrintResult; end Thời gian thực thuậttoán Johnson cài đặt theo cách đánh giá thời gian thực giải thuật xếp Ở ta dùng QuickSort (Trung bình Θ(𝑛 lg 𝑛)) Có thể cài đặt thuậttoán Johnson theo cách khác để tận dụng thuậttoán xếp dãy khóa số, cách làm sau: Chia chi tiết làm hai nhóm: Nhóm 𝑆𝐴 gồm chi tiết 𝑖 có 𝑎𝑖 < 𝑏𝑖 nhóm 𝑆𝐵 gồm chi tiết 𝑗 có 𝑎𝑗 ≥ 𝑏𝑗 Sắp xếp chi tiết nhóm 𝑆𝐴 theo thứ tự tăng dần 𝑎𝑖 , xếp chi tiết nhóm 𝑆𝐵 theo thứ tự giảm dần 𝑏𝑖 nối hai danh sách xếp lại Bài tập 4-1 Bạn người lập lịch giảng dạy cho 𝑛 chuyên đề, chuyên đề thứ 𝑖 cần bắt đầu sau thời điểm 𝑠𝑖 kết thúc thời điểm 𝑓𝑖 : (𝑠𝑖 , 𝑓𝑖 ] Mỗi chuyên đề thời gian diễn gia hoạt động giảng dạy cần phòng học riêng Hãy xếp lịch giảng dạy cho số phòng học phải sử dụng Tìm thuậttoán Ο(𝑛 lg 𝑛) Bài tập 4-2 Trên trục số cho 𝑛 điểm đen 𝑛 điểm trắng hoàn toàn phân biệt Hãy tìm 𝑛 đoạn, đoạn nối điểm đen với điểm trắng, cho hai đoạn thẳng có chung đầu mút tổng độ dài 𝑛 đoạn nhỏ Tìm thuậttoán Ο(𝑛 lg 𝑛) Bài tập 4-3 Xét mã phi tiền tố tập 𝑛 ký tự Nếu ký tự xếp sẵn theo thứ tự không giảm tuần suất xây dựng Huffman thời gian Ο(𝑛) cách sử dụng hai hàng đợi: Tạo nút chứa ký tự đẩy chúng vào hàng đợi theo thứ tự từ nút tần suất thấp tới nút tần suất cao Khởi tạo hàng đợi rỗng, lặp lại thao tác sau 𝑛 − lần: Lấy hai nút 𝑥, 𝑦 có tần suất thấp khỏi hàng đợi cách đánh giá phần tử đầu hai hàng đợi Tạo nút 𝑧 làm nút cha hai nút 𝑥, 𝑦 với tần suất tổng tần suất 𝑥 𝑦 Đẩy 𝑧 vào cuối hàng đợi Chứng minh tính đắn cài đặt thuậttoán Bài tập 4-4 Bài toán xếp ba lô (Knapsack) khảo sát chuyên đề kỹthuật nhánh cận quy hoạch động toán 0/1 Knapsack: Với sản phẩm có hai trạng thái: chọn hay không chọn Chúng ta khảo sát toán Fractional Knapsack: Cho phép chọn phần sản phẩm: Với sản phẩm trọng lượng 𝑤 giá trị 𝑣, ta lấy phần trọng lượng 𝑤 ′ ≤ 𝑤 sản phẩm giá trị 𝑣 ∗ 𝑤′ 𝑣 Chỉ hàm 𝐸𝑠𝑡𝑖𝑚𝑎𝑡𝑒 mục 1.4.3 cho kết tối ưu toán Fractional Knapsack Bài tập 4-5 Giáo sư X lái xe chuyến hành trình “Xuyên Việt” từ Cao Bằng tới Cà Mau dài 𝑙 km Dọc đường có 𝑛 trạm xăng trạm xăng thứ 𝑖 cách Cao Bằng 𝑑𝑖 km Bình xăng xe đổ đầy 𝑚 km Hãy xác định điểm dừng để đổ xăng trạm xăng cho số lần phải dừng đổ xăng hành trình Bài tập 4-6 Cho 𝑛 điểm thực trục số: 𝑥1 , 𝑥2 , … , 𝑥𝑛 ∈ ℝ Hãy đoạn dạng [𝑖, 𝑖 + 1] với 𝑖 số nguyên để phủ hết 𝑛 điểm cho Bài tập 4-7 Bạn có 𝑛 nhiệm vụ, nhiệm vụ cần làm đơn vị thời gian Nhiệm vụ thứ 𝑖 có thời điểm phải hoàn thành (deadline) 𝑑𝑖 bạn hoàn thành nhiệm vụ sau thời hạn 𝑑𝑖 phải khoản phạt 𝑝𝑖 Bạn thời điểm thời điểm thực nhiệm vụ Hãy lên lịch thực nhiệm vụ cho tổng số tiền bị phạt Bài tập 4-8 Một lần Tôn Tẫn đua ngựa với vua Tề Tôn Tẫn vua Tề người có 𝑛 ngựa đánh số từ tới 𝑛, ngựa thứ 𝑖 Tôn Tẫn có tốc độ 𝑎𝑖 , ngựa thứ 𝑖 vua Tề có tốc độ 𝑏𝑖 Luật chơi sau: Có tất 𝑛 cặp đua, cặp đua có ngựa Tôn Tẫn ngựa vua Tề Con ngựa phải tham gia cặp đua Trong cặp đua, ngựa tốc độ cao thắng, hai ngựa có tốc độ kết cặp đua hoà Trong cặp đua, ngựa bên thắng bên điểm, hoà thua điểm Hãy giúp Tôn Tẫn chọn ngựa đấu 𝑛 cặp đua với vua Tề cho hiệu số: Điểm Tôn Tẫn - Điểm vua Tề lớn Tài liệu tham khảo Adelson-Velsky, Georgy Maximovich and Landis, Yevgeniy Mikhailovich An algorithm for the organization of information In Proceedings of the USSR Academy of Sciences ( 1962), 263-266 Aho, Alfred V., Hopcroft, John E., and Ullman, Jeffrey D Data Structures and Algorithms Addison Wesley, 1983 Barahona, Francisco and Tardos, Éva Note on Weintraub's Minimum-Cost Circulation Algorithm SIAM Journal on Computing, 18, (1989), 579-583 Bayer, Rudolf Symmetric binary B-Trees: Data structure and maintenance algorithms Acta Informatica, (1972), 290–306 Bellman, Richard On a Routing Problem Quarterly of Applied Mathematics, 16, (1958), 87-90 Bentley, J.L Solution to Klee's rectangle problems Carnegie-Mellon university, Pittsburgh, PA, 1977 Coppersmith, Don and Winograd, Shmuel Matrix multiplication via arithmetic progressions In STOC '87: Proceedings of the nineteenth annual ACM symposium on Theory of computing (New York, United States 1987), ACM, 1-6 Cormen, Thomas H., Leiserson, Charles Eric, Rivest, Ronald Linn, and Stein, Clifford Introduction to Algorithms MIT Press, 2009 de Berg, Mark, Cheong, Otfried, van Kreveld, Marc, and Overmars, Mark Computational Geometry: Algorithms and Applications Springer-Verlag, 2008 10 Dijkstra, Edsger Wybe A note on two problems in connexion with graphs Numerische Mathematik, (1959), 269-271 11 Ding, Yuzheng and Weiss, Mark A Best case lower bounds for heapsort Computing, 49, (1993), 1-9 12 Dinic, E A Algorithm for solution of a problem of maximum flow in networks with power estimation Soviet Mathematics Doklady, 11, (1970), 1277-1280 13 Edmonds, Jack and Karp, Richard Manning Theoretical improvements in the algorithmic efficiency for network flow problems Journal of the ACM, 19 (1972), 248-264 14 Fenwick, Peter M A New Data Structure for Cumulative Frequency Tables Software: Practice and Experience, 24 (1994), 327-336 15 Fische, Johannes and Heun, Volker A New Succinct Representation of RMQ-Information and Improvements in the Enhanced Suffix Array Lecture Notes in Computer Science, 4614 (2007), 459-470 16 Floyd, Robert W Algorithm 245 - Treesort Communications of the ACM, 7, 12 (1964), 701 17 Ford, Lester Randolph and Fulkerson, Delbert Ray Flows in Networks Princeton University Press, 1962 18 Ford, Lester Randolph and Johnson, Selmer M A Tournament Problem The American Mathematical Monthly, 66, (1959), 387-389 19 Fredman, Michael Lawrence and Tarjan, Robert Endre Fibonacci heaps and their uses in improved network optimization algorithms Journal of the ACM, 34, (1987), 596-615 20 Goldberg, Andrew Vladislav Efficient graph algorithms for sequential and parallel computers MIT, 1987 21 Goldberg, Andrew Vladislav and Robert, Endre Tarjan Finding minimum-cost circulations by canceling negative cycles Journal of the ACM (JACM), 36, (1989), 873886 22 Hoare, Charles Antony Richard QuickSort Computer Journal, 5, (1962), 10-15 23 Hopcroft, John Edward and Karp, Richard Manning An n^(5/2) algorithm for maximum matchings in bipartite graphs SIAM Journal on Computing, 2, (1973), 225-231 24 Hopcroft, John Edward and Tarjan, Robert Endre Efficient algorithms for graph manipulation Communications of the ACM, 16, (1973), 372-378 25 Huffman, David Albert A Method for the Construction of Minimum-Redundancy Codes Proceedings of the Institute of Radio Engineers, 40, (1952), 1098-1101 26 Karatsuba, Anatolii Alexeevich and Ofman, Yu Multiplication of Many-Digital Numbers by Automatic Computers Doklady Akad Nauk SSSR, 145 (1962), 293-294 Translation in Physics-Doklady 7, 595-596, 1963 27 Karp, Manning Richard A characterization of the minimum cycle mean in a digraph Discrete Mathematics , 23, (1978), 309-311 28 Klein, Morton A Primal Method for Minimal Cost Flows with Applications to the Assignment and Transportation Problems Management Science, 14, (1967), 205-220 29 Kruskal, Joseph Bernard On the Shortest Spanning Subtree of a Graph and the Traveling Salesman Problem Proceedings of the American Mathematical Society, 7, (1956), 48-50 30 Kuhn, Harold The Hungarian Method for the assignment problem 31 Lacey, Stephen and Box, Richard A fast, easy sort BYTE, 16, (1991), 315-ff 32 Musser, David R Introspective Sorting and Selection Algorithms Software: Practice and Experience, 27, (1997), 983 - 993 33 Nguyễn, Đức Nghĩa and Nguyễn, Tô Thành Toán Rời Rạc NXB Giáo Dục, 1999 34 Prim, Robert Clay Shortest connection networks and some generalizations Bell System Technical Journal, 36 (1957), 1389-1401 35 Seidel, Raimund G and Aragon, Cecilia R Randomized search trees Algorithmica, 16 (1996), 464-497 36 Shell, Donald L A high-speed sorting procedure Communications of the ACM, 2, (1959), 30-32 37 Sleator, Daniel Dominic and Tarjan, Robert Endre Self-Adjusting Binary Search Trees Journal of the ACM, 32, (1985), 652-686 38 Sokkalingam, P T., Ahuja, Ravindra K., and Orlin, James B New Polynomial-Time CycleCanceling Algorithms for Minimum Cost Flows Network, 36 (1996), 53-63 39 Stoer, Mechthild and Wagner, Frank A simple min-cut algorithm Journal of the ACM (JACM), 44, (1997), 585-591 40 Strassen, Volker Gaussian Elimination is not Optimal Numerische Mathematik, 13, (1969), 354-356 41 Tarjan, Robert Endre Depth first search and linear graph algorithms SIAM Journal on Computing, 1, (1972), 146-160 42 Vuillemin, Jean A data structure for manipulating priority queues Communications of the ACM, 21, (1978), 309-314 43 Williams, J.W.J Algorithm 232: Heapsort Communications of the ACM, 7, (1964), 347348 ... dụng cao hơn, để giải toán liệt kê phức tạp là: Thuật toán quay lui (Back tracking) 1.3 Thuật toán quay lui Thuật toán quay lui dùng để giải toán liệt kê cấu hình Thuật toán làm việc theo cách:... biết Tuy có nhiều nghiên cứu toán Clique người ta chưa tìm thuật toán với độ phức tạp đa thức Ta trình bày cách giải toán Clique thuật toán quay lui kết hợp với kỹ thuật nhánh cận Mô hình duyệt... động thuật toán quay lui end Thuật toán dùng biến