BÀI GIẢNG GIẢI THUẬT VÀ LẬP TRÌNH - QUY HOẠCH ĐỘNG - LÊ MINH HOÀNG - 5 docx

36 567 3
BÀI GIẢNG GIẢI THUẬT VÀ LẬP TRÌNH - QUY HOẠCH ĐỘNG - LÊ MINH HOÀNG - 5 docx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Cấu trúc liệu Giải thuật 131 9.9 NHỮNG NHẬN XÉT CUỐI CÙNG Tìm kiếm thường cơng việc nhanh xếp lại sử dụng nhiều Trên đây, ta trình bày phép tìm kiếm tập hợp để tìm ghi mang khố khố tìm kiếm Tuy nhiên, người ta u cầu tìm ghi mang khố lớn hay nhỏ khố tìm kiếm, tìm ghi mang khố nhỏ mà lớn khố tìm kiếm, tìm ghi mang khố lớn mà nhỏ khố tìm kiếm v.v… Để cài đặt thuật tốn nêu cho trường hợp cần có mềm dẻo định Cũng tương tự xếp, ta khơng nên đánh giá giải thuật tìm kiếm tốt giải thuật tìm kiếm khác Sử dụng thuật tốn tìm kiếm phù hợp với u cầu cụ thể kỹ người lập trình, việc cài đặt nhị phân tìm kiếm hay tìm kiếm số để tìm kiếm vài chục ghi khẳng định điều rõ ràng: khơng biết giải thuật lập trình Bài tập Bài Hãy thử viết chương trình SearchDemo tương tự chương trình SortDemo trước Đồng thời viết thêm vào chương trình SortDemo trước thủ tục TreeSort đánh giá tốc độ thực Bài Tìm hiểu phương pháp tìm kiếm ngồi, cấu trúc B_cây Bài Tìm hiểu phương pháp tìm kiếm chuỗi, thuật tốn BRUTE-FORCE, thuật toán KNUTHMORRIS-PRATT, thuật toán BOYER-MOORE thuật toán RABIN-KARP Tuy gọi chuyên đề "Cấu trúc liệu giải thuật" thực ra, ta tìm hiểu qua hai dạng cấu trúc liệu hay gặp danh sách cây, với số thuật tốn mà "đâu phải có" tìm kiếm xếp Khơng tài liệu đề cập tới cấu trúc liệu giải thuật chúng phong phú liên tục bổ sung Những cấu trúc liệu giải thuật khơng "phổ thơng" lý thuyết đồ thị, hình học, v.v… tách nói kỹ chuyên đề khác Việc sâu nghiên cứu cấu trúc liệu giải thuật, dù phần nhỏ hẹp nảy sinh nhiều vấn đề hay khó, vấn đề lý thuyết độ phức tạp tính tốn, vấn đề NP_đầy đủ v.v… Đó cơng việc nhà khoa học máy tính Nhưng trước trở thành nhà khoa học máy tính điều kiện cần phải biết lập trình Vậy nên tìm hiểu cấu trúc liệu hay giải thuật nào, thiết ta phải cố gắng cài đặt Mọi ý tưởng hay bỏ không biến thành hiệu quả, thực tế Lê Minh Hoàng PHẦN QUY HOẠCH ĐỘNG Các thuật tốn đệ quy có ưu điểm dễ cài đặt, nhiên chất trình đệ quy, chương trình thường kéo theo địi hỏi lớn không gian nhớ khối lượng tính tốn khổng lồ Quy hoạch động (Dynamic programming) kỹ thuật nhằm đơn giản hóa việc tính tốn cơng thức truy hồi cách lưu trữ tồn hay phần kết tính tốn bước với mục đích sử dụng lại Bản chất quy hoạch động thay mơ hình tính tốn “từ xuống” (Top-down) mơ hình tính tốn “từ lên” (Bottom-up) Từ “programming” không liên quan tới việc lập trình cho máy tính, thuật ngữ mà nhà toán học hay dùng để bước chung việc giải dạng toán hay lớp vấn đề Khơng có thuật tốn tổng qt để giải tất tốn quy hoạch động Mục đích phần cung cấp cách tiếp cận việc giải toán tối ưu mang chất đệ quy, đồng thời đưa ví dụ để người đọc làm quen hình thành kỹ việc tiếp cận tốn quy hoạch động 134 Chun đề §1 CƠNG THỨC TRUY HỒI 1.1 VÍ DỤ Cho số tự nhiên n ≤ 100 Hãy cho biết có cách phân tích số n thành tổng dãy số ngun dương, cách phân tích hốn vị tính cách Ví dụ: n = có cách phân tích: 5 5 5 = = = = = = = 1 1 + + + + + + 1 + + + + 1+1+1 1+2 (Lưu ý: n = coi có cách phân tích thành tổng số nguyên dương (0 tổng dãy rỗng)) Để giải toán này, chuyên mục trước ta dùng phương pháp liệt kê tất cách phân tích đếm số cấu hình Bây ta thử nghĩ xem, có cách tính số lượng cách phân tích mà khơng cần phải liệt kê hay khơng ? Bởi số cách phân tích tương đối lớn, phương pháp liệt kê tỏ chậm (n = 100 có 190569292 cách phân tích) Nhận xét: Nếu gọi F[m, v] số cách phân tích số v thành tổng số nguyên dương ≤ m Khi đó: Các cách phân tích số v thành tổng số nguyên dương ≤ m chia làm hai loại: Loại 1: Không chứa số m phép phân tích, số cách phân tích loại số cách phân tích số v thành tổng số nguyên dương < m, tức số cách phân tích số v thành tổng số nguyên dương ≤ m - F[m - 1, v] Loại 2: Có chứa số m phép phân tích Khi cách phân tích loại ta bỏ số m ta cách phân tích số v - m thành tổng số nguyên dương ≤ m (Lưu ý: điều khơng tính lặp lại hốn vị cách) Có nghĩa mặt số lượng, số cách phân tích loại F[m, v - m] Trong trường hợp m > v rõ ràng có cách phân tích loại 1, cịn trường hợp m ≤ v có cách phân tích loại loại Vì thế: F[m, v] = F[m - 1, v] m > v F[m, v] = F[m - 1, v] + F[m, v - m] m ≤ v Ta có cơng thức xây dựng F[m, v] từ F[m - 1, v] F[m, v - m] Công thức có tên gọi cơng thức truy hồi đưa việc tính F[m, v] việc tính F[m', v'] với liệu nhỏ Tất nhiên cuối ta quan tâm đến F[n, n]: Số cách phân tích n thành tổng số nguyên dương ≤ n Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 135 Ví dụ với n = 5, bảng F là: F 0 0 v 1 1 1 1 2 3 1 1 1 m Nhìn vào bảng F, ta thấy F[m, v] tính tổng của: Một phần tử hàng trên: F[m - 1, v] phần tử hàng, bên trái: F[m, v - m] Ví dụ F[5, 5] tính F[4, 5] + F[5, 0], hay F[3, 5] tính F[2, 5] + F[3, 2] Chính để tính F[m, v] F[m - 1, v] F[m, v - m] phải tính trước Suy thứ tự hợp lý để tính phần tử bảng F phải theo thứ tự từ xuống hàng tính theo thứ tự từ trái qua phải Điều có nghĩa ban đầu ta phải tính hàng bảng: F[0, v] = số dãy có phần tử ≤ mà tổng v, theo quy ước đề F[0, 0] = F[0, v] với v > Vậy giải thuật dựng đơn giản: Khởi tạo dòng bảng F: F[0, 0] = F[0, v] với v > 0, sau dùng cơng thức truy hồi tính tất phần tử bảng F Cuối F[n, n] số cách phân tích cần tìm P_3_01_1.PAS * Đếm số cách phân tích số n program Analyse1; {Bài tốn phân tích số} const max = 100; var F: array[0 max, max] of LongInt; n, m, v: Integer; begin Write('n = '); ReadLn(n); FillChar(F[0], SizeOf(F[0]), 0); {Khởi tạo dịng bảng F tồn số 0} F[0, 0] := 1; {Duy có F[0, 0] = 1} for m := to n {Dùng công thức tính dịng theo thứ tự từ xuống dưới} for v := to n {Các phần tử dịng tính theo thứ tự từ trái qua phải} if v < m then F[m, v] := F[m - 1, v] else F[m, v] := F[m - 1, v] + F[m, v - m]; WriteLn(F[n, n], ' Analyses'); {Cuối F[n, n] số cách phân tích} end 1.2 CẢI TIẾN THỨ NHẤT Cách làm tóm tắt lại sau: Khởi tạo dịng bảng, sau dùng dịng tính dịng 1, dùng dịng tính dịng v.v… tới tính hết dịng n Có thể nhận thấy tính xong dịng thứ k việc lưu trữ dòng từ dòng tới dòng k - khơng cần thiết việc tính dịng k + phụ thuộc giá trị lưu trữ dịng k Vậy ta dùng hai mảng chiều: Mảng Current lưu dòng thời xét bảng mảng Next Lê Minh Hoàng 136 Chuyên đề lưu dòng kế tiếp, mảng Current gán giá trị tương ứng dòng Sau dùng mảng Current tính mảng Next, mảng Next sau tính mang giá trị tương ứng dòng Rồi lại gán mảng Current := Next tiếp tục dùng mảng Current tính mảng Next, mảng Next gồm giá trị tương ứng dòng v.v… Vậy ta có cài đặt cải tiến sau: P_3_01_2.PAS * Đếm số cách phân tích số n program Analyse2; const max = 100; var Current, Next: array[0 max] of LongInt; n, m, v: Integer; begin Write('n = '); ReadLn(n); FillChar(Current, SizeOf(Current), 0); Current[0] := 1; {Khởi tạo mảng Current tương ứng với dòng bảng F} for m := to n begin {Dùng dòng thời Current tính dịng Next ⇔ Dùng dịng m - tính dịng m bảng F} for v := to n if v < m then Next[v] := Current[v] else Next[v] := Current[v] + Next[v - m]; Current := Next; {Gán Current := Next tức Current lại lưu phần tử dòng m bảng F} end; WriteLn(Current[n], ' Analyses'); end Cách làm tiết kiệm nhiều không gian lưu trữ, chậm phương pháp phép gán mảng (Current := Next) Có thể cải tiến thêm cách làm sau: P_3_01_3.PAS * Đếm số cách phân tích số n program Analyse3; const max = 100; var B: array[1 2, max] of LongInt;{Bảng B gồm dòng thay cho dòng liên tiếp bảng phương án} n, m, v, x, y: Integer; begin Write('n = '); ReadLn(n); {Trước hết, dòng bảng B tương ứng với dòng bảng phương án F, điền sở quy hoạch động} FillChar(B[1], SizeOf(B[1]), 0); B[1][0] := 1; x := 1; {Dịng B[x] đóng vai trị dịng thời bảng phương án} y := 2; {Dịng B[y] đóng vai trò dòng bảng phương án} for m := to n begin {Dùng dòng x tính dịng y ⇔ Dùng dịng thời bảng phương án để tính dịng kế tiếp} for v := to n if v < m then B[y][v] := B[x][v] else B[y][v] := B[x][v] + B[y][v - m]; x := - x; y := - y; {Đảo giá trị x y, tính xoay lại} end; WriteLn(B[x][n], ' Analyses'); end Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 137 1.3 CẢI TIẾN THỨ HAI Ta cách tốt nữa, bước, ta cần lưu lại dòng bảng F mảng chiều, sau dùng mảng tính lại để sau tính, mảng chiều lưu giá trị bảng F dòng P_3_01_4.PAS * Đếm số cách phân tích số n program Analyse4; const max = 100; var L: array[0 max] of LongInt; {Chỉ cần lưu dòng} n, m, v: Integer; begin Write('n = '); ReadLn(n); FillChar(L, SizeOf(L), 0); L[0] := 1; {Khởi tạo mảng chiều L lưu dòng bảng} for m := to n {Dùng L tính lại nó} for v := m to n L[v] := L[v] + L[v - m]; WriteLn(L[n], ' Analyses'); end 1.4 CÀI ĐẶT ĐỆ QUY Xem lại cơng thức truy hồi tính F[m, v] = F[m - 1, v] + F[m, v - m], ta nhận thấy để tính F[m, v] ta phải biết xác F[m - 1, v] F[m, v - m] Như việc xác định thứ tự tính phần tử bảng F (phần tử tính trước, phần tử tính sau) quan trọng Tuy nhiên ta tính dựa hàm đệ quy mà không cần phải quan tâm tới thứ tự tính tốn Việc viết hàm đệ quy tính cơng thức truy hồi đơn giản, ví dụ ta viết: P_3_01_5.PAS * Đếm số cách phân tích số n dùng đệ quy program Analyse5; var n: Integer; function GetF(m, v: Integer): LongInt; begin if m = then {Phần neo hàm đệ quy} if v = then GetF := else GetF := else {Phần đệ quy} if m > v then GetF := GetF(m - 1, v) else GetF := GetF(m - 1, v) + GetF(m, v - m); end; begin Write('n = '); ReadLn(n); WriteLn(GetF(n, n), ' Analyses'); end Phương pháp cài đặt tỏ chậm phải gọi nhiều lần hàm GetF(m, v) (bài sau giải thích rõ điều này) Ta cải tiến cách kết hợp với mảng hai chiều F Ban đầu phần tử F coi "chưa biết" (bằng cách gán giá trị đặc biệt) Hàm GetF(m, v) gọi trước hết tra cứu tới F[m, v], F[m, v] chưa biết hàm Lê Minh Hồng 138 Chun đề GetF(m, v) gọi đệ quy để tính giá trị F[m, v] dùng giá trị gán cho kết hàm, cịn F[m, v] biết hàm việc gán kết hàm F[m, v] mà khơng cần gọi đệ quy để tính tốn P_3_01_6.PAS * Đếm số cách phân tích số n dùng đệ quy program Analyse6; const max = 100; var n: Integer; F: array[0 max, max] of LongInt; function GetF(m, v: Integer): LongInt; begin if F[m, v] = -1 then {Nếu F[m, v] chưa biết tính F[m, v]} begin if m = then {Phần neo hàm đệ quy} if v = then F[m, v] := else F[m, v] := else {Phần đệ quy} if m > v then F[m, v] := GetF(m - 1, v) else F[m, v] := GetF(m - 1, v) + GetF(m, v - m); end; GetF := F[m, v]; {Gán kết hàm F[m, v]} end; begin Write('n = '); ReadLn(n); FillChar(f, SizeOf(f), $FF); {Khởi tạo mảng F giá trị -1} WriteLn(GetF(n, n), ' Analyses'); end Việc sử dụng phương pháp đệ quy để giải công thức truy hồi kỹ thuật đáng lưu ý, gặp cơng thức truy hồi phức tạp, khó xác định thứ tự tính tốn phương pháp tỏ hiệu quả, làm rõ chất đệ quy công thức truy hồi Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 139 §2 PHƯƠNG PHÁP QUY HOẠCH ĐỘNG 2.1 BÀI TỐN QUY HOẠCH Bài tốn quy hoạch tốn tối ưu: gồm có hàm f gọi hàm mục tiêu hay hàm đánh giá; hàm g1, g2, …, gn cho giá trị logic gọi hàm ràng buộc u cầu tốn tìm cấu hình x thoả mãn tất ràng buộc g1, g2, …gn: gi(x) = TRUE (∀i: ≤ i ≤ n) x tốt nhất, theo nghĩa không tồn cấu hình y khác thoả mãn hàm ràng buộc mà f(y) tốt f(x) Ví dụ: Tìm (x, y) để Hàm mục tiêu : x + y → max Hàm ràng buộc : x2 + y2 ≤ Xét mặt phẳng toạ độ, cặp (x, y) thoả mãn x2 + y2 ≤ tọa độ điểm nằm hình trịn có tâm O gốc toạ độ, bán kính Vậy nghiệm tốn bắt buộc nằm hình trịn Những đường thẳng có phương trình: x + y = C (C số) đường thẳng vng góc với đường phân giác góc phần tư thứ Ta phải tìm số C lớn mà đường thẳng x + y = C có điểm chúng với đường trịn (O, 1) Đường thẳng tiếp tuyến đường tròn: x + y = Tiếp điểm ( 1 , ) tương ứng với nghiệm tối ưu toán cho 2 y x= y= 1 x x+ y = Các dạng toán quy hoạch phong phú đa dạng, ứng dụng nhiều thực tế, cần biết rằng, đa số tốn quy hoạch khơng giải được, chưa giải Cho đến nay, người ta có thuật tốn đơn hình giải tốn quy hoạch tuyến tính lồi, vài thuật tốn khác áp dụng cho lớp toán cụ thể 2.2 PHƯƠNG PHÁP QUY HOẠCH ĐỘNG Phương pháp quy hoạch động dùng để giải tốn tối ưu có chất đệ quy, tức việc tìm phương án tối ưu cho tốn đưa tìm phương án tối ưu số hữu hạn Lê Minh Hoàng 140 Chuyên đề toán Đối với nhiều thuật tốn đệ quy tìm hiểu, ngun lý chia để trị (divide and conquer) thường đóng vai trị chủ đạo việc thiết kế thuật toán Để giải tốn lớn, ta chia làm nhiều tốn dạng với để giải độc lập Trong phương pháp quy hoạch động, nguyên lý thể rõ: Khi cần phải giải toán nào, ta giải tất toán lưu trữ lời giải hay đáp số chúng với mục đích sử dụng lại theo phối hợp để giải tốn tổng qt Đó điểm khác Quy hoạch động phép phân giải đệ quy nội dung phương pháp quy hoạch động: Phép phân giải đệ quy toán lớn phân rã thành nhiều toán giải tốn Việc giải toán lại đưa phép phân rã tiếp thành nhiều toán nhỏ lại giải tiếp tốn nhỏ giải hay chưa Quy hoạch động việc giải tất toán nhỏ ( tốn sở) để từ bước giải toán lớn hơn, giải toán lớn (bài toán ban đầu) Ta xét ví dụ đơn giản: Ví dụ: Dãy Fibonacci dãy số nguyên dương định nghĩa sau: F1 = F2 = 1; ∀ i: ≤ i: Fi = Fi-1 + Fi-2 Hãy tính F6 Xét hai cách cài đặt chương trình: Cách program Fibo1; function F(i: Integer): Integer; begin if i < then F := else F := F(i - 1) + F(i - 2); end; begin WriteLn(F(6)); end Cách program Fibo2; var F: array[1 6] of Integer; i: Integer; begin F[1] := 1; F[2] := 1; for i := to F[i] := F[i - 1] + F[i - 2]; WriteLn(F[6]); end Trong cách 1, ta viết hàm đệ quy F(i) để tính số Fibonacci thứ i Chương trình gọi F(6), gọi tiếp F(5) F(4) để tính … Q trình tính tốn vẽ Ta nhận thấy để tính F(6) phải tính lần F(5), hai lần F(4), ba lần F(3), năm lần F(2), ba lần F(1) Đại học Sư phạm Hà Nội, 1999-2002 152 Chuyên đề Thì F[m, n] phép chèn vừa cộng với số phép biến đổi biến dãy X1…Xm thành dãy Y1…Yn-1: F[m, n] = + F[m, n - 1] b) Hoặc thay vị trí m X ký tự Yn: X1 Y1 …… X2 Y2 …… Xm-1 Yn-1 Xm:=Yn Yn Thì F[m, n] phép thay vừa cộng với số phép biến đổi biến dãy X1…Xm-1 thành dãy Y1…Yn-1: F[m, n] = + F[m-1, n - 1] c) Hoặc xố vị trí thứ m X: X1 Y1 Y2 …… X2 …… Xm-1 Yn-1 Xm Yn Thì F[m, n] phép xố vừa cộng với số phép biến đổi biến dãy X1…Xm-1 thành dãy Y1…Yn: F[m, n] = + F[m-1, n] Vì F[m, n] phải nhỏ có thể, nên trường hợp Xm ≠ Yn F[m, n] = min(F[m, n - 1], F[m - 1, n - 1], F[m - 1, n]) + Ta xây dựng xong công thức truy hồi 3.3.2 Cơ sở quy hoạch động F[0, j] số phép biến đổi biến xâu rỗng thành xâu gồm j ký tự đầu F Nó cần tối thiểu j phép chèn: F[0, j] = j F[i, 0] số phép biến đổi biến xâu gồm i ký tự đầu S thành xâu rỗng, cần tối thiểu i phép xoá: F[i, 0] = i Vậy bảng phương án F (cỡ[0 m, n]) khởi tạo hàng cột sở quy hoạch động Từ dùng cơng thức truy hồi tính tất phần tử bảng B Sau tính xong F[m, n] cho ta biết số phép biến đổi tối thiểu Truy vết: Nếu Xm = Yn việc xét tiếp F[m - 1, n - 1] Nếu không, xét trường hợp: Nếu F[m, n] = F[m, n - 1] + phép biến đổi sử dụng là: Insert(m, Yn) Nếu F[m, n] = F[m - 1, n - 1] + phép biến đổi sử dụng là: Replace(m, Yn) Nếu F[m, n] = F[m - 1, n] + phép biến đổi sử dụng là: Delete(m) Đưa toán với m, n nhỏ truy vết tiếp F[0, 0] Ví dụ: X =' ABCD'; Y = 'EABD' bảng phương án là: Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 153 F 4 1 2 2 2 Hình 50: Truy vết Lưu ý: truy vết, để tránh truy nhập bảng, nên tạo viền cho bảng P_3_03_4.PAS * Biến đổi xâu program StrOpt; const InputFile = 'STR.INP'; OutputFile = 'STR.OUT'; max = 100; var X, Y: String[2 * max]; F: array[-1 max, -1 max] of Integer; m, n: Integer; procedure Enter; var fi: Text; begin Assign(fi, InputFile); Reset(fi); ReadLn(fi, X); ReadLn(fi, Y); Close(fi); m := Length(X); n := Length(Y); end; function Min3(x, y, z: Integer): Integer; {Cho giá trị nhỏ giá trị x, y, z} var t: Integer; begin if x < y then t := x else t := y; if z < t then t := z; Min3 := t; end; procedure Optimize; var i, j: Integer; begin {Khởi tạo viền cho bảng phương án} for i := to m F[i, -1] := max + 1; for j := to n F[-1, j] := max + 1; {Lưu sở quy hoạch động} for j := to n F[0, j] := j; for i := to m F[i, 0] := i; {Dùng cơng thức truy hồi tính tồn bảng phương án} for i := to m for j := to n if X[i] = Y[j] then F[i, j] := F[i - 1, j - 1] else F[i, j] := Min3(F[i, j - 1], F[i - 1, j - 1], F[i - 1, j]) + 1; end; Lê Minh Hoàng 154 Chuyên đề procedure Trace; {Truy vết} var fo: Text; begin Assign(fo, OutputFile); Rewrite(fo); WriteLn(fo, F[m, n]); {F[m, n] số phép biến đổi cần thực hiện} while (m 0) or (n 0) {Vòng lặp kết thúc m = n = 0} if X[m] = Y[n] then {Hai ký tự cuối xâu giống nhau} begin Dec(m); Dec(n); {Chỉ việc truy chéo lên bảng phương án} end else {Tại cần phép biến đổi} begin Write(fo, X, ' -> '); {In xâu X trước biến đổi} if F[m, n] = F[m, n - 1] + then {Nếu phép chèn} begin Write(fo, 'Insert(', m, ', ', Y[n], ')'); Insert(Y[n], X, m + 1); Dec(n); {Truy sang phải} end else if F[m, n] = F[m - 1, n - 1] + then {Nếu phép thay} begin Write(fo, 'Replace(', m, ', ', Y[n], ')'); X[m] := Y[n]; Dec(m); Dec(n); {Truy chéo lên trên} end else {Nếu phép xoá} begin Write(fo, 'Delete(', m, ')'); Delete(X, m, 1); Dec(m); {Truy lên trên} end; WriteLn(fo, ' -> ', X); {In xâu X sau phép biến đổi} end; Close(fo); end; begin Enter; Optimize; Trace; end Bài giải với xâu ≤ 100 ký tự, lưu bảng phương án dạng mảng cấp phát động làm với xâu 255 ký tự (Tốt nên lưu dòng bảng phương án mảng cấp phát động chiều) Hãy tự giải thích giới hạn độ dài liệu 100, lại phải khai báo X Y String[200] String[100] ? 3.4 DÃY CON CÓ TỔNG CHIA HẾT CHO K Cho dãy gồm n (1 ≤ n ≤ 1000) số nguyên dương A1, A2, …, An số nguyên dương k (k ≤ 50) Hãy tìm dãy gồm nhiều phần tử dãy cho cho tổng phần tử dãy chia hết cho k Input: file văn SUBSEQ.INP • Dịng 1: Chứa số n • Dịng 2: Chứa n số A1, A2, …, An cách dấu cách Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 155 Output: file văn SUBSEQ.OUT • Dịng 1: Ghi độ dài dãy tìm • Các dòng tiếp: Ghi phần tử chọn vào dãy • Dịng cuối: Ghi tổng phần tử dãy SUBSEQ.INP 10 11 10 15 20 SUBSEQ.OUT a[10] = a[9] = a[7] = 20 a[6] = 15 a[5] = 10 a[4] = a[3] = 11 a[2] = Sum = 80 3.4.1 Cách giải Đề yêu cầu chọn số tối đa phần tử dãy A để dãy có tổng chia hết cho k, ta giải toán phương pháp duyệt tổ hợp quay lui có đánh giá nhánh cận nhằm giảm bớt chi phí kỹ thuật vét cạn Dưới ta trình bày phương pháp quy hoạch động: Nhận xét 1: Không ảnh hưởng đến kết cuối cùng, ta đặt: Ai := Ai mod k với ∀i: ≤ i ≤ n Nhận xét 2: Gọi S tổng phần tử mảng A, ta thay đổi cách tiếp cận tốn: thay tìm xem phải chọn số tối đa phần tử để có tổng chia hết cho k, ta chọn số tối thiểu phần tử có tổng đồng dư với S theo modul k Khi cần loại bỏ phần tử phần tử lại kết Nhận xét 3: Số phần tử tối thiểu cần loại bỏ nhỏ k Thật vậy, giả sử số phần tử cần loại bỏ m phần tử cần loại bỏ Ai1, Ai2, …, Aim Các phần tử có tổng đồng dư với S theo mô-đun k Xét dãy sau Dãy := () = Dãy rỗng (Tổng ≡ (mod k)) Dãy := (Ai1) Dãy := (Ai1, Ai2) Dãy := (Ai1, Ai2, Ai3) …… Dãy m := (Ai1, Ai2, …, Aim) Như có m + dãy, m ≥ k theo nguyên lý Dirichlet tồn hai dãy có tổng đồng dư theo mơ-đun k Giả sử hai dãy: Ai1 + Ai2 + … + Aip ≡ Ai1 + Ai2 + … + Aip + Aip+1 + … + Aiq (mod k) Lê Minh Hoàng 156 Chuyên đề Suy Aip+1 + … + Aiq chia hết cho k Vậy ta xố hết phần tử dãy chọn mà dãy có tổng đồng dư với S theo modul k, mâu thuẫn với giả thiết dãy chọn có số phần tử tối thiểu Cơng thức truy hồi: Nếu ta gọi F[i, t] số phần tử tối thiểu phải chọn dãy A1, A2, …, Ai để có tổng chia k dư t Nếu khơng có phương án chọn ta coi F[i, t] = +∞ Khi F[i, t] tính qua cơng thức truy hồi sau: Nếu dãy chọn Ai F[i, t] = F[i - 1, t]; Nếu dãy phải chọn Ai F[i, t] = + F[i - 1, t − A i ] ( t − A i hiểu phép trừ lớp đồng dư mod k Ví dụ k = − = ) Từ suy F[i, t] = (F[i - 1, t], + F[i - 1, t - Ai]) Còn tất nhiên, sở quy hoạch động: F(0, 0) = 0; F(0, i) = + ∞ (với ∀i: ≤ i < k) Bảng phương án F có kích thước [0 n, k - 1] tối đa 1001x50 phần tử kiểu Byte P_3_03_5.PAS * Dãy có tổng chia hết cho k program SubSequence; const InputFile = 'SUBSEQ.INP'; OutputFile = 'SUBSEQ.OUT'; maxN = 1000; maxK = 50; var a: array[1 maxN] of Integer; f: array[0 maxN, maxK - 1] of Byte; n, k: Integer; procedure Enter; var fi: Text; i: Integer; begin Assign(fi, InputFile); Reset(fi); ReadLn(fi, n, k); for i := to n Read(fi, a[i]); Close(fi); end; function Sub(x, y: Integer): Integer; {Tính x - y (theo mod k)} var tmp: Integer; begin tmp := (x - y) mod k; if tmp >= then Sub := tmp else Sub := tmp + k; end; procedure Optimize; var i, t: Integer; begin FillChar(f, SizeOf(f), $FF); {Khởi tạo phần tử f[0, ] 255 (+∞)} f[0, 0] := 0; {Ngoại trừ f[0, 0] := 0} Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động for i := for t := if f[i f[i, else f[i, end; 157 to n to k - {Tính f[i, t] := (f[i - 1, t], f[i - 1, Sub(t, ai)] + 1} - 1, t] < f[i - 1, Sub(t, a[i])] + then t] := f[i - 1, t] t] := f[i - 1, Sub(t, a[i])] + 1; procedure Result; var fo: Text; i, t: Integer; SumAll, Sum: LongInt; begin SumAll := 0; for i := to n SumAll := SumAll + a[i]; Assign(fo, OutputFile); Rewrite(fo); WriteLn(fo, n - f[n, SumAll mod k]); {n - số phần tử bỏ = số phần tử giữ lại} i := n; t := SumAll mod k; Sum := 0; for i := n downto if f[i, t] = f[i - 1, t] then {Nếu phương án tối ưu khơng bỏ ai, tức có chọn ai} begin WriteLn(fo, 'a[', i, '] = ', a[i]); Sum := Sum + a[i]; end else t := Sub(t, a[i]); WriteLn(fo, 'Sum = ', Sum); Close(fo); end; begin Enter; Optimize; Result; end 3.4.2 Cách giải Phân phần tử dãy a theo lớp đồng dư modul k Lớp i gồm phần tử chia k dư i Gọi Count[i] số lượng phần tử thuộc lớp i Với ≤ i, t < k; Gọi f[i, t] số phần tử nhiều chọn lớp 0, 1, 2, …, i để tổng chia k dư t Trong trường hợp có cách chọn, gọi Trace[i, t] số phần tử chọn lớp i theo phương án này, trường hợp cách chọn, Trace[i, t] coi -1 Ta dễ thấy f[0, 0] = Count[0], Trace[0, 0] = Count[0], Trace[0, i] với i≠0 -1 Với i ≥ 1; ≤ t < k, có phương án chọn nhiều phần tử lớp từ tới i để tổng chia k dư t phương án chọn j phần tử lớp i (0 ≤ j ≤ Count[i]), bỏ j phần tử đi, phải thu phương án chọn nhiều phần tử lớp từ tới i - để tổng chia k dư t − i * j Từ suy cơng thức truy hồi: Lê Minh Hồng 158 Chuyên đề f [i, t ] = max (f [i − 1, t − j * i] + j) ≤ j≤ Count [ i ] Trace[ i −1, t − j*i ) ≠ −1 Trace[i, t ] = arg max (f [i − 1, t − j * i] + j) ≤ j≤ Count [ i ] Trace[ i −1, t − j*i ) ≠ −1 P_3_03_6.PAS * Dãy có tổng chia hết cho k program SubSequence; const InputFile = 'SUBSEQ.INP'; OutputFile = 'SUBSEQ.OUT'; maxN = 1000; maxK = 50; var a: array[1 maxN] of Integer; Count: array[0 maxK - 1] of Integer; f, Trace: array[0 maxK - 1, maxK - 1] of Integer; n, k: Integer; procedure Enter; var fi: Text; i: Integer; begin Assign(fi, InputFile); Reset(fi); ReadLn(fi, n, k); FillChar(Count, SizeOf(Count), 0); for i := to n begin Read(fi, a[i]); Inc(Count[a[i] mod k]); {Nhập liệu đồng thời với việc tính Count[.]} end; Close(fi); end; function Sub(x, y: Integer): Integer; var tmp: Integer; begin tmp := (x - y) mod k; if tmp >= then Sub := tmp else Sub := tmp + k; end; procedure Optimize; var i, j, t: Integer; begin FillChar(f, SizeOf(f), 0); f[0, 0] := Count[0]; FillChar(Trace, SizeOf(Trace), $FF); {Khởi tạo mảng Trace=-1} Trace[0, 0] := Count[0]; {Ngoại trừ Trace[0, 0] = Count[0]} for i := to k - for t := to k - for j := to Count[i] if (Trace[i - 1, Sub(t, j * i)] -1) and (f[i, t] < f[i - 1, Sub(t, j * i)] + j) then begin f[i, t] := f[i - 1, Sub(t, j * i)] + j; Trace[i, t] := j; end; end; Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 159 procedure Result; var fo: Text; i, t, j: Integer; Sum: LongInt; begin t := 0; {Tính lại Count[i] := Số phần tử phương án tối ưu chọn lớp i} for i := k - downto begin j := Trace[i, t]; t := Sub(t, j * i); Count[i] := j; end; Assign(fo, OutputFile); Rewrite(fo); WriteLn(fo, f[k - 1, 0]); Sum := 0; for i := to n begin t := a[i] mod k; if Count[t] > then begin WriteLn(fo, 'a[', i, '] = ', a[i]); Dec(Count[t]); Sum := Sum + a[i]; end; end; WriteLn(fo, 'Sum = ', Sum); Close(fo); end; begin Enter; Optimize; Result; end Cách giải thứ hai tốt cách giải thứ thực với n lớn Ví dụ cho thấy tốn quy hoạch động có nhiều cách đặt cơng thức truy hồi để giải 3.5 PHÉP NHÂN TỔ HỢP DÃY MA TRẬN Với ma trận A kích thước pxq ma trận B kích thước qxr Người ta có phép nhân hai ma trận để ma trận C kích thước pxr Mỗi phần tử ma trận C tính theo cơng thức: q C ij = ∑ A ik B kj ; ≤ i ≤ p, ≤ j ≤ r k =1 Ví dụ: A ma trận kích thước 3x4, B ma trận kích thước 4x5 C ma trận kích thước 3x5 ⎡1 ⎢5 ⎢ ⎢9 ⎣ Lê Minh Hoàng ⎡1 4⎤ ⎢ ⎥x⎢ ⎥ ⎢3 10 11 12⎥ ⎢ ⎦ ⎣ 1 1 0⎤ ⎡ 14 1⎥ ⎢ ⎥ = 34 1⎥ ⎢ ⎥ ⎢ 54 1⎦ ⎣ 14 22 36 25 100 41 164 ⎤ 21 ⎥ ⎥ 33 ⎥ ⎦ 160 Chuyên đề Để thực phép nhân hai ma trận A(mxn) B(nxp) ta làm đoạn chương trình sau: for i := to p for j := to r begin cij := 0; for k := to q cij := cij + aik * bkj; end; Phí tổn để thực phép nhân đánh giá qua số phép nhân, để nhân hai ma trận A(pxq) B(qxr) ta cần thực p.q.r phép nhân số học Phép nhân ma trận khơng có tính chất giao hốn có tính chất kết hợp (A * B) * C = A * (B * C) Vậy A ma trận cấp 3x4, B ma trận cấp 4x10 C ma trận cấp 10x15 thì: Để tính (A * B) * C, ta thực (A * B) trước, ma trận X kích thước 3x10 sau 3.4.10 = 120 phép nhân số Sau ta thực X * C ma trận kết kích thước 3x15 sau 3.10.15 = 450 phép nhân số Vậy tổng số phép nhân số học phải thực 570 Để tính A * (B * C), ta thực (B * C) trước, ma trận Y kích thước 4x15 sau 4.10.15 = 600 phép nhân số Sau ta thực A * Y ma trận kết kích thước 3x15 sau 3.4.15 = 180 phép nhân số Vậy tổng số phép nhân số học phải thực 780 Vậy trình tự thực có ảnh hưởng lớn tới chi phí Vấn đề đặt tính số phí tổn thực phép nhân dãy ma trận: M1 * M2 * … * Mn Với : M1 ma trận kích thước a1 x a2 M2 ma trận kích thước a2 x a3 … Mn ma trận kích thước an x an+1 Input: file văn MULTMAT.INP • Dịng 1: Chứa số ngun dương n ≤ 100 • Dịng 2: Chứa n + số nguyên dương a1, a2, …, an+1 (∀i: ≤ ≤ 100) cách dấu cách Output: file văn MULTMAT.OUT • Dịng 1: Ghi số phép nhân số học tối thiểu cần thực • Dòng 2: Ghi biểu thức kết hợp tối ưu phép nhân dãy ma trận MULTMAT.INP 3231223 MULTMAT.OUT 31 ((M[1] * (M[2] * M[3])) * ((M[4] * M[5]) * M[6])) Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 161 Trước hết, dãy có ma trận chi phí 0, ta nhận thấy để nhân cặp ma trận khơng có chuyện kết hợp cả, chi phí cho phép nhân tính Vậy phí tổn cho phép nhân hai ma trận liên tiếp dãy hồn tồn ghi nhận lại Sử dụng thông tin ghi nhận để tối ưu hố phí tổn nhân ba ma trận liên tiếp … Cứ tiếp tục ta tính phí tổn nhân n ma trận liên tiếp 3.5.1 Công thức truy hồi: Gọi F[i, j] số phép nhân tối thiểu cần thực để nhân đoạn ma trận liên tiếp: Mi*Mi+1*…*Mj Thì F[i, i] = với ∀i Để tính Mi * Mi+1 * … * Mj, ta có nhiều cách kết hợp: Mi * Mi+1 * … * Mj = (Mi * Mi+1 * … * Mk) * (Mk+1 * Mk+2 * … * Mj) (Với i ≤ k < j) Với cách kết hợp (phụ thuộc vào cách chọn vị trí k), chi phí tối thiểu phải thực bằng: Chi phí thực phép nhân Mi * Mi+1 * … * Mk = F[i, k] Cộng với chi phí thực phép nhân Mk+1 * Mk+2 * … * Mj = F[k + 1, j] Cộng với chi phí thực phép nhân hai ma trận cuối cùng: ma trận tạo thành từ phép nhân (Mi * Mi+1 * … * Mk) có kích thước x ak+1 ma trận tạo thành từ phép nhân (Mk+1 * Mk+2 * … * Mj) có kích thước ak+1 x aj+1, chi phí * ak+1 * aj+1 Từ suy ra: có nhiều cách kết hợp, mà ta cần chọn cách kết hợp để có chi phí nên ta cực tiểu hố F[i, j] theo cơng thức: F[i, j] = (F[i, k ] + F[k + 1, j] + a i * a k +1 * a j+1 ) i≤k < j 3.5.2 Tính bảng phương án Bảng phương án F bảng hai chiều, nhìn vào cơng thức truy hồi, ta thấy F[i, j] tính mà F[i, k] F[k + 1, j] biết Tức ban đầu ta điền sở quy hoạch động vào đường chéo bảng(F[i, i] = 0), từ tính giá trị thuộc đường chéo nằm phía (Tính F[i, i + 1]), lại tính giá trị thuộc đường chéo nằm phía (F[i, i + 2]) … Đến tính F[1, n] dừng lại 3.5.3 Tìm cách kết hợp tối ưu Tại bước tính F[i, j], ta ghi nhận lại điểm k mà cách tính (Mi * Mi+1 * … * Mk) * (Mk+1 * Mk+2 * … * Mj) cho số phép nhân số học nhỏ nhất, chẳng hạn ta đặt T[i, j] = k Khi đó, muốn in phép kết hợp tối ưu để nhân đoạn Mi * Mi+1 * … * Mk * Mk+1 * Mk+2 * … * Mj, ta in cách kết hợp tối ưu để nhân đoạn Mi * Mi+1 * … * Mk cách kết hợp tối ưu Lê Minh Hoàng 162 Chuyên đề để nhân đoạn Mk+1 * Mk+2 * … * Mj (có kèm theo dấu đóng mở ngoặc) đồng thời viết thêm dấu "*" vào hai biểu thức P_3_03_7.PAS * Nhân tối ưu dãy ma trận program MatrixesMultiplier; const InputFile = 'MULTMAT.INP'; OutputFile = 'MULTMAT.OUT'; max = 100; MaxLong = 1000000000; var a: array[1 max + 1] of Integer; F: array[1 max, max] of LongInt; T: array[1 max, max] of Byte; n: Integer; fo: Text; procedure Enter; {Nhập liệu từ thiết bị nhập chuẩn} var i: Integer; fi: Text; begin Assign(fi, InputFile); Reset(fi); ReadLn(fi, n); for i := to n + Read(fi, a[i]); Close(fi); end; procedure Optimize; var i, j, k, len: Integer; x, p, q, r: LongInt; begin for i := to n for j := i to n if i = j then F[i, j] := else F[i, j] := MaxLong; {Khởi tạo bảng phương án: đường chéo = 0, khác = +∞} for len := to n {Tìm cách kết hợp tối ưu để nhân đoạn gồm len ma trận liên tiếp} for i := to n - len + begin j := i + len - 1; {Tính F[i, j]} for k := i to j - {Xét vị trí phân hoạch k} begin {Giả sử ta tính Mi * … * Mj = (Mi * … * Mk) * (Mk+1 * … * Mj)} p := a[i]; q := a[k + 1]; r := a[j + 1]; {Kích thước ma trận nhân cuối cùng} x := F[i, k] + F[k + 1, j] + p * q * r; {Chi phí phân hoạch theo k} if x < F[i, j] then {Nếu phép phân hoạch tốt F[i, j] ghi nhận lại} begin F[i, j] := x; T[i, j] := k; end; end; end; end; procedure Trace(i, j: Integer); {In phép kết hợp để nhân đoạn Mi * Mi+1 * … * Mj} var k: Integer; begin if i = j then Write(fo, 'M[', i, ']') {Nếu đoạn gồm ma trận in ln} else {Nếu đoạn gồm từ ma trận trở lên} begin Write(fo, '('); {Mở ngoặc} Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 163 k := T[i, j]; {Lấy vị trí phân hoạch tối ưu đoạn Mi…Mj} Trace(i, k); {In phép kết hợp để nhân đoạn đầu} Write(fo, ' * '); {Dấu nhân} Trace(k + 1, j); {In phép kết hợp để nhân đoạn sau} Write(fo, ')'); {Đóng ngoặc} end; end; begin Enter; Optimize; Assign(fo, OutputFile); Rewrite(fo); WriteLn(fo, F[1, n]); {Số phép nhân cần thực hiện} Trace(1, n); {Truy vết đệ quy} Close(fo); end 3.6 BÀI TẬP LUYỆN TẬP 3.6.1 Bài tập có gợi ý lời giải Bài Nhập vào hai số nguyên dương n k (n, k ≤ 100) Hãy cho biết a) Có số nguyên dương có ≤ n chữ số mà tổng chữ số k Nếu có tỉ số cần thơng báo có nhiều tỉ b) Nhập vào số p ≤ tỉ Cho biết đem số tìm xếp theo thứ tự tăng dần số thứ p số ? Gợi ý: Câu a: Ta đếm số số có n chữ số mà tổng chữ số (TCCS) k, có điều số ta cho phép bắt đầu Ví dụ: ta coi 0045 số có chữ số mà TCCS Gọi F[n, k] số số có n chữ số mà TCCS k Các số có dạng x1 x2 xn ; x1, x2, …xn chữ số 0…9 x1 + x2 + … + xn = k Nếu cố định x1 = t ta nhận thấy x x n lập thành số có n - chữ số mà TCCS k - t Suy x1 nhận giá trị từ tới nên mặt số lượng: F[n, k] = ∑ F [n − 1, k − t ] Đây công thức truy t =0 hồi tính F[n, k], thực xét giá trị t từ tới t ≤ k mà (để tránh trường hợp k - t 109 ta đặt lại phần tử 109 + để tránh bị tràn số cộng hai số q lớn Kết thúc q trình tính tốn, F[n, k] = 109 + ta cần thơng báo chung chung có > tỉ số Cơ sở quy hoạch động đặt là: F[1, k] = số số có chữ số mà TCCS k, k ≥ 10 F[1, k] = cịn ≤ k ≤ F[1, k] = Câu b: Dựa vào bảng phương án F[0 n, k], Lê Minh Hoàng 164 Chuyên đề F[n - 1, k] = số số có n - CS mà TCCS k = số số có n CS, bắt đầu 0, TCCS k F[n - 1, k - 1] = số số có n - CS mà TCCS k - = số số có n CS, bắt đầu 1, TCCS k F[n - 1, k - 2] = số số có n - CS mà TCCS k - = số số có n CS, bắt đầu 2, TCCS k … F[n - 1, k - 9] = số số có n - CS mà TCCS k - = số số có n CS, bắt đầu 9, TCCS k Từ ta biết số thứ p (theo thứ tự tăng dần) cần tìm có chữ số chữ số nào, tương tự ta tìm chữ số thứ hai, thứ ba v.v… số Bài Cho n gói kẹo (n ≤ 200), gói chứa khơng q 200 viên kẹo, số M ≤ 40000 Hãy cách lấy số gói kẹo để tổng số kẹo M, thông báo khơng thể thực việc Gợi ý: Giả sử số kẹo chứa gói thứ i Ai Gọi b[V] số nguyên dương bé thoả mãn: Có thể chọn số gói kẹo từ gói đến gói b[V] số gói để tổng số kẹo V Nếu khơng có phương án chọn, ta coi b[V] = +∞ Trước tiên, khởi tạo b[0] = b[V] = +∞ với V > Ta xây dựng b[V] sau: Để tiện nói, ta đặt k = b[V] Vì k bé có thể, nên có cách chọn số gói kẹo từ gói đến gói k để số kẹo V chắn phải chọn gói k Mà chọn gói k số gói kẹo từ đến k - 1, phải chọn số gói để số kẹo V - Ak Tức b[V - Ak] ≤ k - < k Vậy b[V] tính cách: Xét tất gói kẹo k có Ak ≤ V thoả mãn b[V - Ak] < k, chọn số k bé nhất, sau gán b[V] := k Đây cơng thức truy hồi tính bảng phương án Sau tính b[1], b[2], …, b[M] Nếu b[M] +∞ có nghĩa khơng có phương án chọn Nếu khơng chọn gói p1 = b[M], chọn gói p2 = b[M - Ap1], lại chọn gói p3 = b[M - Ap1 - Ap2]… Đến truy vết tới b[0] thơi Bài Cho n gói kẹo (n ≤ 200), gói chứa khơng q 200 viên kẹo, chia gói kẹo làm hai nhóm cho số kẹo hai nhóm chênh lệch Gợi ý: Gọi S tổng số kẹo M nửa tổng số kẹo, áp dụng cách giải Sau Tìm số ngun dương T thoả mãn: • T≤M • Tồn cách chọn số gói kẹo để tổng số kẹo T (b[T] ≠ +∞) • T lớn Đại học Sư phạm Hà Nội, 1999-2002 Quy hoạch động 165 Sau chọn số gói kẹo để T viên kẹo, gói kẹo đưa vào nhóm, số cịn lại vào nhóm thứ hai Bài Cho bảng A kích thước m x n, ghi số nguyên Một người xuất phát ô cột 1, cần sang cột n (tại ô được) Quy tắc: Từ ô A[i, j] quyền sang ô A[i, j + 1]; A[i - 1, j + 1]; A[i + 1, j + 1] Hãy tìm vị trí xuất phát hành trình từ cột sang cột n cho tổng số ghi đường lớn A = 6 7 Gợi ý: Gọi B[i, j] số điểm lớn có tới ô A[i, j] Rõ ràng ô cột B[i, 1] = A[i, 1]: A = 6 7 B = Với ô (i, j) cột khác Vì (i, j - 1), (i - 1, j - 1), (i + 1, j - 1) sang ô (i, j), sang ô (i, j) số điểm cộng thêm A[i, j] Chúng ta cần B[i, j] số điểm lớn nên B[i, j] = max(B[i, j - 1], B[i - 1, j - 1], B[i + 1, j - 1]) + A[i, j] Ta dùng công thức truy hồi tính tất B[i, j] Cuối chọn B[i, n] phần tử lớn cột n bảng B từ truy vết tìm đường nhiều điểm 3.6.2 Bài tập tự làm Bài Bài tốn túi với kích thước nêu khơng thực tế, chẳng có siêu thị có ≤ 100 gói hàng Hãy lập chương trình giải tốn túi với n ≤ 10000; M ≤ 1000 Bài Xâu ký tự S gọi xâu xâu ký tự T xố bớt số ký tự xâu T để xâu S Lập chương trình nhập vào hai xâu ký tự S1, S2 Tìm xâu S3 có độ dài lớn xâu S1 S2 Ví dụ: S1 = 'abcdefghi123'; S2 = 'abc1def2ghi3' S3 'abcdefghi3' Bài Một xâu ký tự X gọi chứa xâu ký tự Y xố bớt số ký tự xâu X để xâu Y: Ví dụ: Xâu '1a2b3c45d' chứa xâu '12345' Một xâu ký tự gọi đối xứng Lê Minh Hồng 166 Chun đề khơng thay đổi ta viết ký tự xâu theo thứ tự ngược lại: Ví dụ: 'abcABADABAcba', 'MADAM' xâu đối xứng Nhập xâu ký tự S có độ dài khơng q 128, tìm xâu ký tự T thoả mãn điều kiện: Đối xứng Chứa xâu S Có ký tự (có độ dài ngắn nhất) Nếu có nhiều xâu T thoả mãn đồng thời điều kiện cần cho biết Chẳng hạn với S = 'a_101_b' chọn T = 'ab_101_ba' hay T = 'ba_101_ab' Ví dụ: S T MADAM MADAM Edbabcd edcbabcde 00_11_22_33_222_1_000 000_11_222_33_222_11_000 abcdefg_hh_gfe_1_d_2_c_3_ba ab_3_c_2_d_1_efg_hh_gfe_1_d_2_c_3_ba Bài Có n loại tiền giấy: Tờ giấy bạc loại i có mệnh giá V[i] ( n ≤ 20, ≤ V[i] ≤ 10000) Hỏi muốn mua hàng giá M có cách trả số tiền loại giấy bạc cho (Trường hợp có > tỉ cách cần thơng báo có nhiều tỉ) Nếu tồn cách trả, cho biết cách trả phải dùng tờ tiền Bài Cho n quân đô-mi-nô xếp dựng đứng theo hàng ngang đánh số từ đến n Quân đômi-nô thứ i có số ghi a[i] số ghi b[i] Xem hình vẽ: 1 4 6 1 1 Biết ≤ n ≤ 100 ≤ ai, bi ≤ với ∀i: ≤ i ≤ n Cho phép lật ngược quân đô-mi-nô Khi quân đô-mi-nô thứ i bị lật, có số ghi b[i] số ghi ô a[i] Vấn đề đặt tìm cách lật quân đô-mi-nô cho chênh lệch tổng số ghi hàng tổng số ghi hàng dướii tối thiểu Nếu có nhiều phương án lật tốt nhau, phương án phải lật qn Như ví dụ lật hai quân Đô-mi-nô thứ thứ Khi đó: Tổng số hàng = + + + + + = 17 Tổng số hàng = + + + + + = 17 Bài Xét bảng H kích thước 4x4, hàng cột đánh số A, B, C, D Trên 16 ô bảng, ô ghi ký tự A B C D Đại học Sư phạm Hà Nội, 1999-2002 ... hiệu quả, làm rõ chất đệ quy công thức truy hồi Đại học Sư phạm Hà Nội, 199 9-2 002 Quy hoạch động 139 §2 PHƯƠNG PHÁP QUY HOẠCH ĐỘNG 2.1 BÀI TỐN QUY HOẠCH Bài tốn quy hoạch tốn tối ưu: gồm có hàm... 199 9-2 002 Quy hoạch động 151 STR.INP PBBCEFATZQABCDABEFA STR.OUT PBBCEFATZ -> Delete(9) -> PBBCEFAT PBBCEFAT -> Delete(8) -> PBBCEFA PBBCEFA -> Insert(4, B) -> PBBCBEFA PBBCBEFA -> Insert(4, A) -> ... pháp quy hoạch động gọi tốn quy hoạch động Cơng thức phối hợp nghiệm tốn để có nghiệm tốn lớn gọi cơng thức truy hồi (hay phương trình truy tốn) quy hoạch động Tập tốn nhỏ có lời giải để từ giải

Ngày đăng: 13/08/2014, 20:22

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan