Chđề BD HS giỏi Tin 12

78
1 Chuyên Đề: PHÂN TÍCH THIẾT KẾ THUẬT TOÁN PHẦN 1: THUẬT TOÁN VÀ PHÂN TÍCH THUẬT TOÁN I. KHÁI NIỆM BÀI TOÁN VÀ THUẬT TOÁN 1. Khái niệm bài toán Trong phạm vi tin học, ta có thể quan niệm bài toán là một việc nào đó ta muốn máy tính thực hiện. Một bài toán được mô tả bởi hai thành phần: tập dữ liệu đầu vào cho trước (Input) và tập kết quả ra cần nhận được (Output). Thuật ngữ “một” bài toán được hiểu theo nghĩa phổ dụng, bài toán có nhiều bộ dữ liệu khác nhau. Ví dụ: giải bài toán tìm ước chung lớn nhất (UCLN) của hai số nguyên dương M, N. Thuật toán để giải bài toán này phải đáp ứng với bất kì bộ giá trị cụ thể của bộ dữ liệu vào M, N. 2. Khái niệm thuật toán: Việc cho một bài toán là mô tả rõ Input và Output. Vấn đề là làm thế nào để tìm ra Ouput?. Đó chính là lời giải của bài toán Có nhiều xu hướng khác nhau trong việc nghiên cứu lời giải của các bài toán. Trong toán học có xu hướng nghiên cứu định tính, chứng minh sự tồn tại lời giải bài toán mà không cần thiết chỉ ra một cách tường minh cách tìm lời giải đó.Trong việc nghiên cứu thuật toán ở đây, chúng ta quan tâm tới cách tường minh để tìm Output từ Input của bài toán. Có nhiều định nghĩa khác nhau về thuật toán, dưới đây là một định nghĩa thường dùng Thuật toán để giải một bài toán là một dãy hữu hạn các thao tác được sắp xếp theo một trình tự xác định sao cho sau khi thực hiện dãy thao tác ấy, từ Input của bài toán, ta nhận được Output cần tìm 3. Các bƣớc giải bài toán trên máy tính: a. Xác định bài toán: Việc xác định bài toán chính là xác định rõ hai thành phần Input, Output và mối liên hệ giữa chúng. Ví dụ: giải bài toán tìm ước chung lớn nhất (UCLN) của hai số nguyên dương M, N. Input: hai số nguyên dương M, N Output: UCLN của M và N

description

Tai lieu hay cho HS chuyen Tin

Transcript of Chđề BD HS giỏi Tin 12

Page 1: Chđề BD HS giỏi Tin 12

1

Chuyên Đề:

PHÂN TÍCH THIẾT KẾ THUẬT TOÁN

PHẦN 1: THUẬT TOÁN VÀ PHÂN TÍCH THUẬT TOÁN

I. KHÁI NIỆM BÀI TOÁN VÀ THUẬT TOÁN

1. Khái niệm bài toán

Trong phạm vi tin học, ta có thể quan niệm bài toán là một việc nào đó ta muốn máy tính

thực hiện.

Một bài toán được mô tả bởi hai thành phần: tập dữ liệu đầu vào cho trước (Input) và tập

kết quả ra cần nhận được (Output).

Thuật ngữ “một” bài toán được hiểu theo nghĩa phổ dụng, bài toán có nhiều bộ dữ liệu khác

nhau.

Ví dụ: giải bài toán tìm ước chung lớn nhất (UCLN) của hai số nguyên dương M, N. Thuật

toán để giải bài toán này phải đáp ứng với bất kì bộ giá trị cụ thể của bộ dữ liệu vào M, N.

2. Khái niệm thuật toán:

Việc cho một bài toán là mô tả rõ Input và Output. Vấn đề là làm thế nào để tìm ra Ouput?.

Đó chính là lời giải của bài toán

Có nhiều xu hướng khác nhau trong việc nghiên cứu lời giải của các bài toán. Trong toán

học có xu hướng nghiên cứu định tính, chứng minh sự tồn tại lời giải bài toán mà không cần

thiết chỉ ra một cách tường minh cách tìm lời giải đó.Trong việc nghiên cứu thuật toán ở đây,

chúng ta quan tâm tới cách tường minh để tìm Output từ Input của bài toán.

Có nhiều định nghĩa khác nhau về thuật toán, dưới đây là một định nghĩa thường dùng

Thuật toán để giải một bài toán là một dãy hữu hạn các thao tác được sắp xếp theo một

trình tự xác định sao cho sau khi thực hiện dãy thao tác ấy, từ Input của bài toán, ta nhận được

Output cần tìm

3. Các bƣớc giải bài toán trên máy tính:

a. Xác định bài toán:

Việc xác định bài toán chính là xác định rõ hai thành phần Input, Output và mối liên hệ giữa

chúng.

Ví dụ: giải bài toán tìm ước chung lớn nhất (UCLN) của hai số nguyên dương M, N.

Input: hai số nguyên dương M, N

Output: UCLN của M và N

Page 2: Chđề BD HS giỏi Tin 12

2

b. Thiết kế hoặc lựa chọn thuật toán:

Một bài toán có thể có nhiều thuật toán để giải. Nhưng một thuật toán thì chỉ có thể dùng để

giải quyết cho một bài toán cụ thể mà thôi. Vấn đề đặt ra ở đây nếu có nhiều thuật toán để giải

một bài toán thì ta nên chọn thuật toán nào? Dĩ nhiên ta sẽ chọn thuật toán nào tốt nhất. Nhưng

một thuật toán thế nào được cho là tốt nhất, nó được đánh giá dựa trên những tiêu chí nào?

Tùy theo bài toán mà ta sẽ lựa chọn thuật toán theo các tiêu chí sau đây:

(1) Tính hiệu quả về thời gian: là thời gian để thực hiện xong một bài toán

(2) Tính hiệu quả về không gian: là dung lượng cần thiết để lưu trữ các dữ liệu cần thiết khi

xử lí bài toán

(3) Tính khả thi khi cài đặt thuật toán: là thuật toán có dễ cài đặt hay không, có tốn nhiều

công sức của người lập trình không.

Ví dụ: Đối với một bài toán lớn và số lần giải bài toán là nhiều lần thì tiêu chí (1) sẽ được

ưu tiên hàng đầu. Nhưng đối với bài toán nhỏ và số lần giải bài toán đó không nhiều thì thông

thường ta sẽ chọn tiêu chí (3) làm tiêu chí được chọn đầu tiên

c. Viết chương trình:

Viết chương trình là tổng hợp giữa việc lựa chọn cách tổ chức dữ liệu và sử dụng ngôn ngữ

lập trình để diễn đạt đúng thuật toán

Cấu trúc dữ liệu (Data Structures): là các đơn vị cấu trúc (construct) của ngôn ngữ lập

trình dùng để biểu diễn các mô hình dữ liệu, ví dụ như mảng (array), bản ghi (record), tệp

(file), xâu (string), danh sách liên kết (list linked),…

Các cấu trúc dữ liệu được lựa chọn không những cần có khả năng biểu diễn được dữ liệu

vào Input và dữ liệu ra Output của bài toán cần giải mà còn cần phải phù hợp với các thao tác

của thuật toán và cài đặt được bằng ngôn ngữ lập trình đã được lựa chọn.

Hai bước b và c thường được thực hiện không tách rời và gắn kết chặt chẽ với nhau rất chặt

chẽ. Vì có những thuật toán chỉ thích ứng và hiệu quả với một cách tổ chức dữ liệu nhất định.

Như vậy có thuật toán tốt chưa đủ mà phải chọn được cấu trúc dữ liệu thích hợp, đồng thời phải

có kĩ thuật cài đặt chương trình tốt.

d. Kiểm thử và hiệu chỉnh:

Sau khi được viết xong, chương trình vẫn còn có thể có nhiều lỗi khác chưa phát hiện được

nên có thể không có kết quả đúng. Vì vậy, cần phải kiểm thử tính đúng đắn của chương trình.

Có nhiều cách để thực hiện được điều đó, cách thường dùng là thực hiện chương trình với một

số bộ Input tiêu biểu phù thuộc vào đặc thù của bài toán mà bằng cách nào đó ta đã biết trước

được bộ kết quả Output tương ứng. Các bộ Input và Output tương ứng này gọi là các Test.

Trong quá trình kiểm thử nếu phát hiện có sai sót, ta phải hiệu chỉnh lại chương trình rồi thử

lại. Trên cơ sở đánh giá các lỗi của chương trình qua kiểm thử có thể xác định được hoặc ngôn

ngữ lập trình, hoặc cấu trúc dữ liệu thậm chí cả thuật toán lựa chọn là không phù hợp, cần thiết

quay lại các công việc của bước trước đó.

Page 3: Chđề BD HS giỏi Tin 12

3

e. Viết tài liệu

Tài liệu trình bày các nội dung liên quan đến mô tả bài toán, thuật toán, cấu trúc dữ liệu,

thiết kế chương trình và hướng dẫn sử dụng. Tài liệu (bao gồm cả các chú thích trong chương

trình) là cần thiết không những cho người dùng để có thể khai thác tốt chương trình, đề xuất

phương án để hoàn thiện chương trình mà còn cho chính người lập trình hoặc người đọc

chương trình dễ dàng chỉnh sửa, nâng cấp chương trình khi cần thiết

II. PHÂN TÍCH THUẬT TOÁN

1. Độ phức tạp của thuật toán:

Một trong những tiêu chí thường được lấy để đánh giá thuật toán là thời gian thực hiện thuật

toán. Vậy, làm thế nào để “đo được” thời gian thực hiện của một chương trình (mô tả một thuật

toán)?

a. Dùng mẫu chuẩn

Đây là cách rất tự nhiên và thường được sử dụng. Dựa vào thời gian thực tế thực hiện các

chương trình viết bằng cùng một ngôn ngữ lập trình cho các thuật toán khác nhau với cùng các

bộ dữ liệu vào Input như nhau, trên cùng một hệ thống máy tính để kết luận thuật toán nào

nhanh, thuật toán nào chậm. Tuy nhiên việc này cũng dễ mắc lỗi vì khi thực hiện chương trình,

ngoài yếu tố thuật toán còn các yếu tố khác của hệ thống, đặc trưng dữ liệu chạy thử cũng có

ảnh hưởng đến thời gian chạy của chương trình. Vì thế để so sánh chính xác hơn cần xây dựng

các bộ dữ liệu vào Input theo các mẫu chuẩn (benchmark) sao cho có thể thừa nhận một

chương trình thực hiện tốt trên mẫu chuẩn thì được coi là tốt trên mọi dữ liệu vào.

b. Phân tích thuật toán

Một cách khác để đánh giá chương trình (thuật toán) là dựa vào mỗi câu lệnh của chương

trình nguồn sẽ thực hiện bao nhiêu lần trên một tập dữ liệu vào. Phần lớn các môi trường lập

trình đều có bộ đếm lệnh (statement couter) là công cụ để thực hiện phép đo đó.

Mục tiêu của phân tích thuật toán không chỉ là để so sánh, đánh giá giúp cho việc lựa chọn

thuật toán tốt mà còn dựa vào kết quả phân tích đánh giá đó để hiệu chỉnh, cải tiến thuật toán

đã có được tốt hơn. Nhiều chương trình cho thấy, tổng thời gian thực hiện một chương trình lớn

thường tiêu phí cho những phần “nhỏ” của chương trình. Về nguyên tắc, số các thao tác trừu

tượng trong thuật toán là rất lớn. Tuy nhiên thường thì tính năng của thuật toán chỉ phục thuộc

vào một vài đại lượng. Khi đánh giá thời gian thực hiện thuật toán ta chú ý đặc biệt đến các

phép toán mà số lần thực hiện không ít hơn các phép toán khác (chứa trong các phần “nhỏ” của

chương trình).

Việc chú ý đến các phép toán được thực hiện nhiều lần là điều cần thiết khi cài đặt thuật

toán bằng chương trình. Ví dụ, người lập trình cần quan tâm lớn để cải tiến các “vòng lặp

trong” vì các phép toán trong đó là các phép toán được thực hiện lặp lại nhiều lần nhất.

Cách đánh giá thời gian thực hiện thuật toán độc lập với hệ thống máy tính dẫn tới khái

niệm độ phức tạp của thuật toán. Thời gian để thực hiện thuật toán phụ thuộc rất nhiều yếu tố.

Một yếu tố rất quan trọng là kích thước của dữ liệu vào. Dữ liệu càng lớn thì thời gian thực

hiện thuật toán càng lớn. Ta kí hiệu T(n) là hàm “đo” số lượng các phép toán cơ bản xuất hiện

khi thực hiện thuật toán và đố số nguyễn phúc thảo ngọc là kích thước dữ liệu vào

Page 4: Chđề BD HS giỏi Tin 12

4

2. Kí pháp độ phức tạp của thuật toán

Ta có thể gọi hàm T(n) là thời gian thực hiện một thuật toán nào đó. Giải sử, f(n) và g(n) là

các hàm xác định dương với mọi n. Khi đó ta nói độ phức tạp tính toán của thuật toán có thời

gian thực hiện là T(n) là:

Hàm O lớn (đọc là ô lớn): O(g(n)) nếu tồn tại các hằng số phan văn mạnh và n0 sao cho

T(n) ≤ c.g(n) với mọi n ≥ n0 và gọi là kí pháp chữ O lớn, hàm g(n) được gọi là giới hạn trên

của hàm T(n).

Ví dụ: nếu T(n) = n2 + 1 thì T(n) = O(n

2).

Thật vậy, chọn c = 2 và n0 = 1, khi đó mọi n ≥ 1, ta có:

T(n) = n2 + 1 ≤ 2n

2 = 2g(n)

3. Các cách xác định độ phức tạp của thuật toán

a. Quy tắc hằng số

Nếu một thuật toán T có thời gian thực hiện T(n) = O (c1f(n)) với c1 là một hằng số dương

thì có thể coi thuật toán T đó có độ phức tạp tính toán là O (f(n)).

Thật vậy, vì T(n) = O(c1f(n)) nên tồn tại c0 > 0 và n0 >0 để T(n) ≤ c0.c1f(n) với mọi n ≥ n0.

Chọn c = c0.c1, với mọi n > n0 ta có T(n) ≤ cf(n). Vậy quy tắc hằng số là đúng.

Như vậy, hằng số ở đây không quan trọng.

b. Quy tắc cộng

Giả thiết, thuật toán gồm hai phần liên tiếp T1 và T2. Khi đó nếu phần T1 của thuật toán có

thời gian thực hiện là T1(n) = O(f(n)) và phần T2 có thời gian thực hiện là T2(n) = O(g(n)), khi

đó thời gian thực hiện thuật toán sẽ là

T1(n) + T2(n) = O(f(n) + g(n))

Chứng minh: Vì T1(n) = O(f(n)) nên tồn tại các hằng số c1 và n1 sao cho

T1(n) ≤ c1.f(n) với mợi n ≥ n1

Vì T2(n) = O(g(n)) nên tồn tại các hằng số c2 và n2 sao cho

T2(n) ≤ c2.f(n) với mợi n ≥ n2

Chọn c = max(c1, c2) và n0 = max(n1, n2) ta có với mọi n ≥ n0:

T(n) = T1(n) + T2(n) ≤ c1f(n) + c2g(n) ≤ cf(n) + cg(n) = c(f(n) + g(n))

Đó là điều phải chứng minh

c. Quy tắc lấy max

Nếu thuật toán T có thời gian thực hiện T(n) = O(f(n) + g(n)) thì có thể coi thuật toán T có

độ phức tạp tính toán là O(max(f(n),g(n))).

Thật vậy, vì T(n) = O(f(n)+g(n)) nên tồn tại n0 > 0 và c > 0 để T(n) ≤ cf(n) + cg(n), với mọi

n ≥ n0

Vậy T(n) ≤ cf(n) + cg(n) ≤ 2c.max(f(n), g(n)) với mọi n ≥ n0

Page 5: Chđề BD HS giỏi Tin 12

5

Từ đó suy ra điều cần chứng minh

d. Quy tắc nhân

Nếu đoạn thuật toán T có thời gian thực hiện T(n) = O(f(n)). Khi đó nếu thực hiện k(n) lần

đoạn thuật toán T với k(n) = O(g(n)) thì độ phức tập tính toán sẽ là: O(f(n).g(n))

Chứng minh: Thời gian thực hiện k(n) lần đoạn thuật toán T sẽ là k(n)T(n), theo định nghĩa:

- Tồn tại ck≥0 và nk>0 để k(n)≤ckg(n) với mợi n≥nk

- Tồn tại cT≥0 và nT>0 để T(n)≤cTf(n) với mọi n≥nT

Vậy với mọi n ≥ max(nT,nk) ta có k(n)T(n) ≤ ckcT(f(n)g(n)). Từ đó suy ra điều cần chứng

minh.

4. Các thuật ngữ thƣờng dùng cho độ phức tạp thuật toán

Độ phức tạp Thuật ngữ

O(1) Độ phức tạp hằng số

O(logn) Độ phức tạp lôgarit

O(n) Độ phức tạp tuyến tính

O(nlogn) Độ phức tạp nlogn

O(nb) Độ phức tạp đa thức

O(bn) (b>1) Độ phức tạp hàm mũ

O(n!) Độ phức tạp giai thừa

5. Đánh giá thời gian thực hiện chƣơng trình

Trước hết, ta phân loại câu lệnh trong ngôn ngữ lập trình bậc cao. Có các loại câu lệnh

thường dùng sau:

- Câu lệnh đơn thực hiện một thao tác, ví dụ câu lệnh gán đơn giản (không chứa lời gọi

hàm trong biểu thức), đọc/ghi đơn giản, câu lệnh chuyển điều khiển đơn giản (break, goto,

continue, return)

- Câu lệnh hợp thành là dãy các câu lệnh tạo thành một khối độc lập

- Câu lệnh rẽ nhánh dạng IF…THEN (còn gọi là câu lệnh điều kiện IF)

- Các câu lệnh lặp

Để đánh giá thời gian thực hiện chương trình, cần thiết phải biết đánh giá thời gian thực hiện

các câu lệnh. Để làm điều đó ta áp dụng các quy tắc tính độ phức tạp thuật toán đã trình bày ở

mục trên, cụ thể:

- Thời gian thực hiện một lệnh đơn không phụ thuộc vào kích thước dữ liệu nên sẽ là O(1)

- Thời gian thực hiện một câu lệnh hợp thành sẽ được tính theo quy tắc cộng và quy tắc

max

- Thời gian thực hiện câu lệnh IF: giả sử thời gian thực hiện hai câu lệnh thành phần dạng

đủ là f(n) và g(n) thì thời gian thực hiện của câu lệnh IF sẽ được tính theo quy tắc max nên sẽ là

O(max(f(n),g(n))).

Page 6: Chđề BD HS giỏi Tin 12

6

- Thời gian thực hiện câu lệnh lặp sẽ áp dụng theo quy tắc nhân, nghĩa là O(k(n)f(n)),

trong đó k(n) là số lần lặp và f(n) là thời gian thực hiện câu lện bên trong vòng lặp

6. Một số ví dụ

Phân tích thời gian thực hiện của các đoạn chương trình sau:

Ví dụ 1:

VAR i, j, n: longint;

s1, s2: longint;

BEGIN

{1} Readln(n);

{2} s1:=0;

{3} FOR i:=1 TO n DO

{4} s1:=s1+i;

{5} s2:=0;

{6} FOR j:=1 TO n DO

{7} s2:=s2+j*j;

{8} writeln(‘1+2+…+’,n,’=’,s1);

{9} writeln(‘1^2+2^2+..+’,n,’^2=’,s2);

END.

Thời gian thực hiện chương trình phụ thuộc vào n

Các câu lệnh {1}, {2}, {4}, {5}, {7}, {8}, {9} có thời gian thực hiện là O(1)

Câu lệnh {3} thời gian thực hiện là O(n). Câu lệnh {6} thời gian thực hiện là O(n)

Vậy thời gian thực hiện của chương trình là

Max(O(1), O(1), O(n), O(1), O(n),O(1), O(1)) = O(n)

Ví dụ 2:

{1} c:=0;

{2} FOR i:=1 TO 2*n DO

{3} c:=c+1

{4} FOR i:=1 TO n DO

{5} FOR j:=1 TO n DO

{6} c:=c+1

Câu lệnh {1}. {3}, {6} có thời gian thực hiện là O(1). Lệnh lặp FOR {2} có số lần lặp là 2n

nên lệnh {2} có thời gian thực hiện là O(n). Lệnh lặp 5 có số lần lặp là n nên lệnh {5} có thời

gian thực hiện là O(n). Lệnh lặp {4} có số lần lặp là n và nó chứ câu lệnh lặp {5} nên thời có

thời gian thực hiện là O(n2).

Vậy thời gian thực hiện của đoạn chương trình trên là

Max(O(1), O(n), O(n2)) = O(n

2)

Page 7: Chđề BD HS giỏi Tin 12

7

Ví dụ 3:

{1} FOR i:=1 TO n DO

{2} FOR j:=1 TO i DO

{3} c:=c+1

Câu lệnh {3} có thời gian thực hiện là O(1)

Khi i=1, j chạy từ 1 đến 1 nên lệnh lặp For {2} lặp 1 lần

Khi i:=2, j chạy từ 1 đến 2 nên lệnh lặp For {2} lặp 2 lần

Khi i:=n, j chạy từ 1 đến n nên lệnh lặp For {2} lặp n lần

Như vậy lệnh {3} được lặp 1+2+3…+n=n(n+1)/2 lần, do đó lệnh {1} có thời gian thực hiện

là O(n2)

Vậy thời gian thực hiện đoạn chương trình trên là O(n2)

BÀI TẬP

Phân tích thời gian thực hiện của các đoạn chƣơng trình sau:

Câu 1.

Readln(n);

S:=0;

i:=1;

WHILE i<n DO

BEGIN

Readln(X);

S:= S + X;

Inc(i);

END;

M:= S/n;

Writeln(M);

Câu 2.

FOR i := 1 TO n DO

FOR j := 1 TO n DO

BEGIN

c[i,j] := 0;

FOR k := 1 TO n DO

c[i,j] := c[i,j] + a[i,k] * b[k,j];

END;

Page 8: Chđề BD HS giỏi Tin 12

8

Câu 3.

j:=n;

d:=0;

REPEAT

j:=j div 2;

d:=d+1;

UNTIL j<1

Câu 4.

FOR i:=1 TO n DO

IF i mod 2 =0 THEN c:=c+1

ELSE c:=c+2;

Câu 5.

FOR i:=1 TO n DO

IF i mod 2 = 0 THEN

FOR j:=1 TO n DO DO c:=c+1;

Câu 6.

D:=0

FOR i:=1 TO n-2 DO

FOR j:=i+1 TO n-1 DO

FOR k:=j+1 TO n DO d:=d+1;

Page 9: Chđề BD HS giỏi Tin 12

9

PHẦN 2: THIẾT KẾ THUẬT TOÁN

I. THUẬT TOÁN DUYỆT

Như chúng ta đã biết các thuật toán được xây dựng để giải quyết vấn đề nhờ vào một quy

tắc nào đó. Tuy nhiên có những vấn đề không theo quy tắc, và khi đó phải dùng phương pháp

vét cạn.

Vét cạn, duyệt, quay lui… là một số tên gọi tuy không đồng nghĩa nhưng cùng chỉ một

phương pháp rất đơn giản trong tin học: tìm nghiệm của một bài toán bằng cách xem xét tất cả

các phương án có thể. Đối với con người phương pháp này thường là không khả thi vì số

phương án cần kiểm tra quá lớn. Tuy nhiên đối với máy tính, nhờ tốc độ xử lí nhanh, máy tính

có thể giải rất nhiều bài toán bằng phương pháp vét cạn.

Ưu điểm lớn nhất của phương pháp vét cạn là luôn đảm bảo tìm ra nghiệm chính xác. Ngoài

ra phương pháp vét cạn còn có một số ưu điểm so với các phương pháp khác là đòi hỏi rất ít bộ

nhớ và cài đặt đơn giản. Hạn chế duy nhất của phương pháp này là thời gian thực thi rất lớn, độ

phức tạp thường ở bậc mũ. Do đó vét cạn thường chỉ áp dụng tốt với các bài toán có kích thước

nhỏ.

Mặc dù vậy, không nên coi thường phương pháp này. Rất nhiều bài toán chỉ có thuật toán

duy nhất là vét cạn. Trong một số tình huống khác, chẳng hạn như thời gian lập trình hạn chế

thì vét cạn có thể coi như một giải pháp tình thế. Rất nhiều trường hợp ta có thể sử dụng vét

cạn theo phương châm: thà mất 1tiếng để viết một chương trình vét cạn chạy trong trong 4

tiếng, còn hơn mất 4 ngày tìm thuật toán hiệu qủa để chương trình chạy trong 1 phút.

Chúng ta không đề cập kĩ về việc áp dụng phương pháp vét cạn đối với các bài toán đơn

giản như tìm giá trị nhỏ nhất, lớn nhất hay tìm tất cả các số nguyên tố của một tập hợp. Chúng

ta sẽ xem xét thuật toán vét cạn đối với các bài toán tìm cấu hình tổ hợp và bài toán tối ưu tổ

hợp, là lớp các bài toán rất tổng quát và phổ biến trong tin học

1. Phƣơng pháp:

Trong nhiều bài toán, việc tìm nghiệm có thể quy về việc tìm vectơ hữu hạn (x1,x2,…,xn,

…), độ dài vectơ có thể xác định trước hoặc không. Vectơ này cần phải thỏa mãn một số điều

kiện tùy thuộc vào yêu cầu của bài toán. Các thành phần xi được chọn ra từ tập hữu hạn Ai.

Tùy từng trường hợp mà bài toán có thể yêu cầu: tìm một nghiệm, tìm tất cả các nghiệm

hoặc đếm số nghiệm.

Tư tưởng của thuật toán quay lui vét cạn như sau: Ta xây dựng vectơ nghiệm từng bước, bắt

đầu từ vecto không (). Thành phần đầu tiên x1 được chọn ra từ tập S1=A1. Giả sử đã chọn được

các thành phần x1, x2, …, xi. Lặp lại quá trình trên để tiếp tục mở rộng nghiệm. Nếu không thể

chọn được thành phần xi+1 (Si+1 rỗng) thì ta quay lại chọn một phần tử khác của Si cho xi. Nếu

không còn một phần tử nào khác của Si ta quay lại chọn một phần tử khác của Si-1 làm xi-1 và

cứ thế tiếp tục. Trong quá trình mở rộng nghiệm, ta phải kiểm tra nghiệm đang xây dựng đã là

nghiệm của bài toán chưa. Nếu chỉ cần tìm một nghiệm thì khi gặp nghiệm ta dừng lại. Còn nếu

cần tìm tất cả các nghiệm thì quá trình đó chỉ dừng lại khi tất cả các khả năng lựa chọn của

thành phần của vectơ nghiệm đã được vét cạn.

Thuật toán duyệt thường được dùng mô hình đệ quy sau:

Page 10: Chđề BD HS giỏi Tin 12

10

PROCEDURE Try(i)

BEGIN

<xác định Si>

FOR xi Si DO BEGIN

<ghi nhận thành phần thứ i>

IF (tìm thấy nghiệm) THEN <đưa ra nghiệm>

ELSE Try(i+1)

<loại phần tử thứ i>

END;

END;

Hoặc ta có thể sử dụng mô hình đệ quy quay lui như sau:

PROCEDURE Try(i)

BEGIN

FOR (mỗi phương án chọn) DO

IF (chọn được) THEN

BEGIN

<Thực hiện bước đi thứ i>

IF (thành công) THEN (thông báo kết quả)

ELSE Try(i+1)

<Hủy bước đi thứ i>

END;

END;

Với mô hình tổng quát trên ta có ba vấn đề trọng tâm cần làm là:

- Tìm cách biểu diễn nghiệm của bài toán dưới dạng một dãy các đối tượng được chọn

dần từng bước;

- Xác định tập Si các ứng cử viên được chọn làm thành phần tứ i của nghiệm. Chọn cách

thích hợp để biễu diễn Si;

- Tìm các điều kiện để một vectơ đã được chọn làm nghiệm bài toán.

2. Giải các bài toán cấu hình tổ hợp bằng thuật toán duyệt

a. Tổ hợp:

Một tổ hợp chập k của n là một tập con k phần tử của tập n phần tử.

Số lượng tổ hợp chập k của n được tính theo công thức sau:

k)!(nk!

n!Ck

n

Chẳng hạn tập {1,2,3,4} có các tổ hợp chập 2 là: {1,2}, {1,3, {1,4, {2,3}, {2,4}, {3,4}. Vì

trong tập hợp các phần tử không phân biệt thứ tự nên tập {1,2} cũng là tập {2,1} và do đó, ta

coi chúng chỉ là một tổ hợp.

Bài toán đặt ra cho chúng ta là hãy xác định tất cả các tổ hợp châp k của tập n phần tử. Để

đơn giản ta chỉ xét bài toán tìm các tổ hợp của tập các số nguyên từ 1 đến n. Đối với một tập

Page 11: Chđề BD HS giỏi Tin 12

11

hữu hạn bất kì, bằng cách đánh số thứ tự của các phần tử, ta cũng đưa được về bài toán đối với

tập các số nguyên từ 1 đến n.

Nghiệm cần tìm của bài toán tìm các tổ hợp chập k của n phần tử phải thoả mãn các điều

kiện sau:

- Là một vector x =(x1,x2,…xk)

- xi lấy giá trị trong tập {1,2,…n}

- Ràng buộc: xi<xi+1 với mọi giá trị i từ 1 đến k-1.

Có ràng buộc 3 là vì tập hợp không phân biệt thứ tự phần tử nên ta sắp xếp các phần tử theo

thứ tự tăng dần.

Để giải quyết bài toán này ta cần xác định 3 vấn đề quan trọng

- Biểu diễn nghiệm: xây dựng một mảng x để biểu diễn tổ hợp

- Xác định tập Si: là từ xi-1+1 đến (n-k+i). Để điều này đúng cho cả trường hợp i = 1 ta

thêm vào x0=0

- Điều kiện để một vectơ được chọn là: x[i1]< x[i] nki.

Dưới đây là toàn bộ chương trình giải bài toán trên. Để đơn giản, các giá trị n,k được nhập

từ bàn phím và các tổ hợp được in ra màn hình. Người đọc có thể cải tiến chương trình để

nhập/xuất ra file.

PROGRAM tohop;

USES crt;

CONST

max = 20;

VAR

n,k : integer;

x : array[0..max] of integer;

{===============================}

PROCEDURE Input;

BEGIN

clrscr;

write('n,k = '); readln(n,k);

writeln('Cac TO hop chap ',k,' cua ',n);

END;

{===============================}

PROCEDURE print;

VAR

i : integer;

BEGIN

FOR i := 1 TO k DO write(' ',x[i]);

writeln;

END;

{===============================}

PROCEDURE try(i:integer);

VAR j : integer;

BEGIN

Page 12: Chđề BD HS giỏi Tin 12

12

FOR j := x[i-1]+1 TO n-k+i DO

BEGIN

x[i] := j;

IF i = k THEN Print ELSE try(i+1);

END;

END;

{===============================}

PROCEDURE solve;

BEGIN

x[0] := 0;

try(1);

END;

{===============================}

BEGIN

Input;

solve;

END.

b. Chỉnh hợp lặp:

Chỉnh hợp lặp chập k của n là một dãy k thành phần, mỗi thành phần là một phần tử của tập n

phần tử, có xét đến thứ tự và không yêu cầu các thành phần khác nhau.

Số lượng chỉnh hợp lặp chập k của n được tính theo công thức sau:

kk

n nA

Một ví dụ dễ thấy nhất của chỉnh hợp lặp là các dãy nhị phân. Một dãy nhị phân độ dài m là

một chỉnh hợp lặp chập m của tập 2 phần tử {0,1}. Chẳng hạn 101 là một dãy nhị phân độ dài

3. Ngoài ra ta còn có 7 dãy nhị phân độ dài 3 nữa là 000, 001, 010, 011, 100, 110, 111. Vì có

xét thứ tự nên dãy 101 và dãy 011 là 2 dãy khác nhau.

Như vậy, bài toán xác định tất cả các chỉnh hợp lặp chập k của tập n phần tử yêu cầu tìm

các nghiệm như sau:

- Là một vector x =(x1,x2,…xk)

- xi lấy giá trị trong tập {1,2,…n}

- Không có ràng buộc nào giữa các thành phần.

Chú ý là cũng như bài toán tìm tổ hợp, ta chỉ xét đối với tập n số nguyên từ 1 đến n. Nếu tập

hợp cần tìm chỉnh hợp không phải là tập các số nguyên từ 1 đến n thì ta có thể đánh số các

phần tử của tập đó để đưa về tập các số nguyên từ 1 đến n

Do không có ràng buộc nào giữa các thành phần nên đối với bài này chỉ cần dùng một mảng

x để lưu nghiệm

Dưới đây là chương trình

PROGRAM Chinhhoplap;

USES crt;

CONST max = 20;

VAR

Page 13: Chđề BD HS giỏi Tin 12

13

n : integer;

x : array[1..max] of integer;

{===============================}

PROCEDURE Input;

BEGIN

clrscr;

write('n = '); readln(n);

writeln('Cac day nhi phan DO dai ',n);

END;

{===============================}

PROCEDURE print;

VAR i : integer;

BEGIN

FOR i := 1 TO n DO write(' ',x[i]);

writeln;

END;

{===============================}

PROCEDURE try(i:integer);

VAR j : integer;

BEGIN

FOR j := 0 TO 1 DO

BEGIN

x[i] := j;

IF i = n THEN Print ELSE try(i+1);

END;

END;

{===============================}

PROCEDURE solve;

BEGIN

try(1);

END;

{===============================}

BEGIN

Input;

solve;

END.

c. Chỉnh hợp không lặp:

Khác với chỉnh hợp lặp là các thành phần được phép lặp lại, tức là có thể giống nhau, chỉnh

hợp không lặp chập k của tập n phần tử cũng là một dãy k thành phần lấy từ tập n phần tử có

xét thứ tự nhưng các thành phần không được phép giống nhau.

Số lượng chỉnh hợp không lặp chập k của n được tính theo công thức sau:

k)!(n

n!A

k

n

Chẳng hạn có n người, một cách chọn ra k người để xếp thành một hàng là một chỉnh hợp

không lặp chập k của n.

Page 14: Chđề BD HS giỏi Tin 12

14

Một trường hợp đặc biệt của chỉnh hợp không lặp là hoán vị. Hoán vị của một tập n phần tử

là một chỉnh hợp không lặp chập n. Nói một cách trực quan thì hoán vị của tập n phần tử là

phép thay đổi vị trí của các phần tử (do đó mới gọi là hoán vị).

Nghiệm của bài toán tìm các chỉnh hợp không lặp chập k của tập n số nguyên từ 1 đến n là

các vector x thoả mãn các điều kiện:

- x có k thành phần: x = (x1,x2,…xk)

- Các giá trị xi lấy trong tập {1,2,..n}

- Ràng buộc: các giá trị xi đôi một khác nhau, tức là xixj với mọi ij.

Chỉnh hợp không lặp yêu cầu các phần tử phải khác nhau. Để đảm bảo điều đó, ngoài mảng

x, ta sẽ dùng thêm một cấu trúc dữ liệu nữa là mảng d để đánh dấu. Khi một giá trị được chọn,

ta đánh dấu giá trị đó, và khi chọn, ta chỉ chọn các giá trị chưa đánh dấu. Mảng d sẽ là "trạng

thái" của thuật toán.

Dưới đây là chương trình tìm toàn bộ hoán vị của n số nguyên từ 1 đến n

PROGRAM Hoanvi;

USES crt;

CONST max = 20;

VAR n : integer;

x,d : array[1..max] of integer;

{===============================}

PROCEDURE Input;

BEGIN

clrscr;

write('n = '); readln(n);

writeln('Cac hoan vi cua day ',n);

END;

{===============================}

PROCEDURE print;

VAR i : integer;

BEGIN

FOR i := 1 TO n DO write(' ',x[i]);

writeln;

END;

{===============================}

PROCEDURE try(i:integer);

VAR j : integer;

BEGIN

FOR j := 1 TO n DO

IF d[j] = 0 THEN

BEGIN

x[i] := j; d[j] := 1;

IF i = n THEN Print ELSE try(i+1);

d[j] := 0;

END;

END;

{===============================}

Page 15: Chđề BD HS giỏi Tin 12

15

PROCEDURE solve;

BEGIN

try(1);

END;

{===============================}

BEGIN

Input;

solve;

END.

3. Bài toán 8 quân hậu

Yêu cầu bài toán: Cho bàn cờ vua nxn. Hãy xếp n con hậu lên bàn cờ sao cho không con

nào khống chế con nào. Hai 2 con hậu khống chế nhau nếu chúng ở trên cùng một hàng, một

cột hoặc một đường chéo.

Chẳng hạn khi n =8 ta có một cách đặt sau, các ô đen là các vị trí đặt hậu:

Để chuyển bài toán này về dạng chuẩn của bài toán tìm cấu hình tổ hợp, ta có có nhận xét:

mỗi con hậu phải ở trên một hàng và một cột. Do đó ta coi con hậu thứ i ở hàng i và nếu biết

x[i] là cột đặt con hậu thứ i thì ta suy ra được lời giải. Vậy nghiệm của bài toán có thể coi là

một vector x gồm n thành phần với ý nghĩa:

- Con hậu thứ i được đặt ở hàng i và cột x[i].

- x[i] lấy giá trị trong tập {1,2…n}

- Ràng buộc: các giá trị x[i] khác nhau từng đôi một và không có 2 con hậu ở trên cùng

một đường chéo.

Để cài đặt bài toán này, chúng ta sẽ phân tích chi tiết về các ràng buộc trên.

Ràng buộc thứ nhất là các giá trị x[i] phải khác nhau. Ta có thể dùng một mảng đánh dấu

như ở thuật toán hoán vị để đảm bảo điều này.

Ràng buộc thứ 2 là các con hậu không được nằm trên cùng một đường chéo chính và phụ.

Ta dễ dàng nhận ra rằng 2 vị trí (x1,y1) và (x2,y2) nằm trên cùng đường chéo chính nếu:

x1y1=x2y2=const.

Tương tự, 2 vị trí (x1,y1) và (x2,y2) nằm trên cùng đường chéo phụ nếu:

x1y1=x2y2=const

Page 16: Chđề BD HS giỏi Tin 12

16

Do đó, con hậu i đặt tại vị trí (i,x[i]) và con hậu j đặt tại vị trí (j,x[j]) phải thoả mãn ràng

buộc:

ix[i] jx[j] và i+x[i] j+x[j] với mọi ij

Ta có thể viết riêng một hàm Ok để kiểm tra các ràng buộc đó. Nhưng giải pháp tốt hơn là

dùng thêm các mảng đánh dấu để mô tả rằng một đường chéo chính và phụ đã có một con hậu

khống chế. Tức là khi ta đặt con hậu i ở vị trí (i,j), ta sẽ đánh dấu đường chéo chính i-j và

đường chéo phụ i+j.

Như vậy về cấu trúc dữ liệu, ta dùng 4 mảng:

- Mảng x với ý nghĩa: x[i] là cột ta sẽ đặt con hậu ở hàng thứ i.

- Mảng cot với ý nghĩa: cot[j]=1 nếu cột j đã có một con hậu được đặt, ngược lại thì

cot[j]=0.

- Mảng dcc với ý nghĩa: dcc[k]=1 nếu đường chéo chính thứ k đã có một con hậu được

đặt, tức là ta đã đặt một con hậu tại vị trí (i,j) mà ij=k; ngược lại thì dcc[k]=0.

- Tương tự ta dùng mảng dcp với ý nghĩa: dcp[k]=1 nếu đường chéo phụ thứ k đã có một

con hậu được đặt.

Dưới đây là chương trình

CONST n=8;

TYPE vector = array[1..n] of longint;

VAR cot:array[1..n] of longint;

Dcc:array[1-n..n-1] of longint;

Dcp:array[1+1..n+n] of longint;

X:vector;

PROCEDURE print;

VAR i:longint;

BEGIN

FOR i:=1 TO n DO write(x[i],’’);

Writeln;

END;

PROCEDURE xephau(i:longint);

VAR j:longint;

BEGIN

FOR j:=1 TO n DO

IF (cot[j]=0) and (dcc[i-j]=0) and (dcp[i+j]=0) THEN

BEGIN

X[i]:=j;

Cot[j]:=1;

Dcc[i-j]=1;

Dcp[i+j]:=1;

IF j=n THEN print ELSE xephau(j+1);

Cot[j]:=0;

Dcc[i-j]:=0;

Dcp[i+j]:=0;

END;

END;

Page 17: Chđề BD HS giỏi Tin 12

17

BEGIN

Fillchar(cot,sizeof(cot),0);

Fillchar(dcc,sizeof(dcc),0);

Fillchar(dcp,sizeof(dcp),0);

Xephau(1);

Readln;

END;

Bài này có tất cả 92 nghiệm. Một trong những nghiệm đó là (1, 5, 8, 6, 3, 7, 2, 4)

BÀI TẬP

Câu 1. Bài toán từ đẹp

Tìm tất cả các từ đẹp độ dài n

Một từ đẹp là một xâu độ dài n chỉ gồm các kí tự A,B,C mà không có 2 xâu con liên tiếp

nào giống nhau.

Chẳng hạn ABAC là một từ đẹp độ dài 4, BABCA là một từ đẹp độ dài 5.

Câu 2. Bài toán rút tiền tự động

Một máy ATM hiện có n (n<=20) tờ tiền có mệnh giá t1, t2, .., tn. Hãy đưa ra một cách trả

với số tiền đúng bằng S, nếu không thể trả đúng bằng S thì thông báo không chọn được

Câu 3. Bài toán xếp ba lô

Có một balô có tải trọng m và n đồ vật, đồ vật i có trọng lượng wi và có giá trị vi. Hãy lựa

chọn các vật để cho vào balô sao cho tổng trọng lượng của chúng không quá M và tổng giá trị

của chúng là lớn nhất.

Câu 4. Bài toán ngƣời du lịch

Có n thành phố, d[i,j] là chi phí để di chuyển từ thành phố i đến thành phố j. (Nếu không có

đường đi thì d[i,j] = ). Một người muốn đi du lịch qua tất cả các thành phố, mỗi thành phố

một lần rồi trở về nơi xuất phát sao cho tổng chi phí là nhỏ nhất. Hãy xác định một đường đi

như vậy.

Câu 5. Bài toán mã đi tuần

Cho một bàn cờ có kích thước nxn (n>=3). Một con mã di chuyển theo luật cờ vua được đặt

trong 1 ô với tọa độ đầu là (x1, y1). Hãy tìm một đường đi với n2-1 bước đi sao cho trên mọi ô

trên bàn cờ đều được mã nhảy đến đúng 1 lần

Câu 6. Viết chương trình liệt kê tất cả các xâu nhị phân có đúng n chữ số (n>=3) sao cho

không có xâu con 101

Câu 7. Có n cặp vợ chồng. Hãy xếp họ vào 1 bàn tròn gồm 2n chiếc ghế sao cho

- Không có ghế thừa

- Nam nữ ngồi xen kẻ

- Vợ chồng không được ngồi cạnh nhau

Page 18: Chđề BD HS giỏi Tin 12

18

II. THUẬT TOÁN SẮP XẾP

1. Tầm quan trọng của bài toán sắp xếp:

Sắp xếp một danh sách các đối tượng theo một thứ tự nào đó là một bài toán thường được

vận dụng trong các ứng dụng tin học. Ví dụ ta cần sắp xếp danh sách thí sinh theo tên với thứ

tự Alphabet, hoặc sắp xếp danh sách sinh viên theo điểm trung bình với thứ tự từ cao đến thấp.

Một ví dụ khác là khi cần tìm kiếm một đối tượng trong một danh sách các đối tượng bằng giải

thuật tìm kiếm nhị phân thì danh sách các đối tượng này phải được sắp xếp trước đó.

Tóm lại sắp xếp là một yêu cầu không thể thiếu trong khi thiết kế các phần mềm. Do đó

việc nghiên cứu các phương pháp sắp xếp là rất cần thiết để vận dụng trong khi lập trình.

2. Sắp xếp trong và sắp xếp ngoài

Sắp xếp trong là sự sắp xếp dữ liệu được tổ chức trong bộ nhớ trong của máy tính, ở đó ta

có thể sử dụng khả năng truy nhập ngẫu nhiên của bộ nhớ và do vậy nó thực hiện rất nhanh.

Sắp xếp ngoài là sự sắp xếp được sử dụng khi số lượng đối tượng cần sắp xếp lớn không thể

lưu trữ trong bộ nhớ trong mà phải lưu trữ trên bộ nhớ ngoài. Cụ thể là ta sẽ sắp xếp dữ liệu

được lưu trữ trong các tập tin.

3. Phát biểu bài toán:

Giả sử các đối tượng cần được sắp xếp là các mẩu tin gồm một hoặc nhiều trường. Một

trong các trường được gọi là khóa sắp xếp (key), kiểu của nó là một kiểu có quan hệ thứ tự

(như các kiểu số nguyên, số thực, chuỗi ký tự...).

Danh sách các đối tượng cần sắp xếp sẽ là một mảng của các mẩu tin vừa nói ở trên. Mục

đích của việc sắp xếp là tổ chức lại các mẩu tin sao cho các khóa của chúng được sắp thứ tự

tương ứng với quy luật sắp xếp.

Ví dụ: Cho mảng a các đối tượng, cần sắp xếp lại các thành phần (phần tử) của mảng a để

nhận được mảng (dãy) a mới với các phần tử có giá trị khóa tăng dần:

a[1].key ≤ a[2].key ≤ … ≤ a[n].key

Ðể trình bày các ví dụ minh họa chúng ta sẽ dùng PASCAL làm ngôn ngữ thể hiện và sử

dụng khai báo sau:

CONST Max = 1000;

TYPE Object = Record

Key : KeyTYPE;

OtherFields : OtherTYPE;

END

Tarray = aray [1..Max]of Object

VAR a: Tarray;

n:longint;

PROCEDURE Swap(VAR x,y:Object);

VAR temp : Object;

BEGIN

temp := x; x := y; y := temp;

END;

Cần thấy rằng thủ tục Swap có thời gian thực hiện là O(1)

Page 19: Chđề BD HS giỏi Tin 12

19

4. Các thuật toán sắp xếp đơn giản:

a. Sắp xếp nổi bọt (sắp xếp tráo đổi - Bubble Sort):

* Ý tưởng: Chúng ta tưởng tượng rằng các mẩu tin được lưu trong một mảng dọc, qua quá

trình sắp xếp, mẫu tin nào có khóa “nhẹ” sẽ được nổi lên trên. Chúng ta duyệt tòan mảng, từ

dưới lên trên. Nếu hai phần tử ở cạnh nhau mà không đúng thứ tự tức là nếu phần tử “nhẹ hơn”

lại nằm dưới thì phải cho nó “nổi lên” bằng cách đổi chỗ hai phần tử này cho nhau. Việc này

lặp lại cho đến khi không còn phần tử nào đứng sai thứ tự.

* Mô tả cụ thể:

- Lượt thứ nhất: Xét các phần tử từ a[n] đến a[2], với mỗi phần tử a[j], so sánh khoá của nó

với khoá của phần tử a[j-1] đứng ngay trước nó. Nếu khoá của a[j] nhỏ hơn khoá của a[j-1] thì

hoán đổi a[j] và a[j-1] cho nhau. Sau luợt thứ nhất phần tử nhỏ có khóa nhỏ nhất sẽ “nổi lên” vị

trí thứ nhất

- Lượt thứ 2: Xét các phần tử từ a[n] đến a[3], và làm tương tự như trên. Sau lượt thứ 2

phần tử có khóa nhỏ thứ 2 sẽ “nổi lên” vị trí thứ 2

- Lượt thứ n-1: thì dãy đã được sắp xếp xong

Ví dụ: Sắp xếp mảng gồm 10 mẩu tin có khóa là các số nguyên: 5, 6, 2, 2, 10, 12, 9, 10, 9

và 3

Bảng sau ghi lại các giá trị khoá tương ứng với từng lượt thực hiện thuật toán trên.

Khóa

Lượt a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a[10]

Ban đầu 5 6 2 2 10 12 9 10 9 3

Lượt 1 2 5 6 2 3 10 12 9 10 9

Lượt 2 2 5 6 3 9 10 12 9 10

Lượt 3 3 5 6 9 9 10 12 10

Lượt 4 5 6 9 9 10 10 12

Lượt 5 6 9 9 10 10 12

Lượt 6 9 9 10 10 12

Lượt 7 9 10 10 12

Lượt 8 10 10 12

Lượt 9 10 12

Kết quả 2 2 3 5 6 9 9 10 10 12

Page 20: Chđề BD HS giỏi Tin 12

20

* Chương trình:

PROCEDURE BubbleSort;

VAR i,j: integer;

BEGIN

FOR i := 1 TO n-1 DO

FOR j := n DOWNTO i+1 DO

IF a[j].key < a[j-1].key THEN

Swap(a[j],a[j-1]);

END;

* Đánh giá độ phức tạp:

Số phép toán so sánh a[j].key > a[j-1].key được dùng để đánh giá hiệu suất về mặt thời gian

cho thuật toán sắp xếp nổi bọt. Tại lượt thứ i cần n – i phép so sánh. Như vậy tổng số phép so

sánh cần thiết là:

)(2

1)n(n1...2)(n1)(ni)(nT(n) 2

1n

1i

nO

Vậy thuật toán có độ phức tạp là T(n) = )( 2nO

* Một cách cài đặt khác của thuật toán trên cũng cho độ phức tạp O(n2)

FOR i := 1 TO n-1 DO

FOR j := i+1 TO n DO

IF a[j].key < a[i].key THEN

Swap(a[j],a[i]);

b. Sắp xếp chọn (Selection Sort):

* Ý tưởng: Tìm phần tử có khóa nhỏ nhất trong dãy sau đó tráo đổi nó với phần tử đầu tiên

của dãy. Sau đó lại tiếp tục tìm phần tử có khóa nhỏ nhất (trừ phần tử đầu tiên) rồi tráo đổi với

phần tử thứ 2. Việc này lặp lại cho đến khi không còn phần tử để tráo đổi.

* Mô tả cụ thể:

- Ðầu tiên chọn phần tử có khóa nhỏ nhất trong n phần tử từ a[1] đến a[n] và hoán vị nó với

phần tử a[1].

- Chọn phần tử có khóa nhỏ nhất trong n-1phần tử từ a[2] đến a[n] và hoán vị nó với a[2].

- Tổng quát ở bước thứ i, chọn phần tử có khoá nhỏ nhất trong n-i+1 phần tử từ a[i] đến a[n]

và hoán vị nó với a[i].

- Sau n-1 bước này thì mảng đã được sắp xếp.

Ví dụ: Sắp xếp mảng gồm 10 mẩu tin có khóa là các số nguyên: 5, 6, 2, 2, 10, 12, 9, 10, 9

và 3

Page 21: Chđề BD HS giỏi Tin 12

21

Bảng sau ghi lại các giá trị khoá tương ứng với từng lượt thực hiện thuật toán trên.

Khóa

Lượt a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a[10]

Ban đầu 5 6 2 2 10 12 9 10 9 3

Lượt 1 2 6 5 2 10 12 9 10 9 3

Lượt 2 2 5 6 10 12 9 10 9 3

Lượt 3 3 6 10 12 9 10 9 5

Lượt 4 5 10 12 9 10 9 6

Lượt 5 6 12 9 10 9 10

Lượt 6 9 12 10 9 10

Lượt 7 9 10 12 10

Lượt 8 10 12 10

Lượt 9 10 12

Kết quả 2 2 3 5 6 9 9 10 10 12

* Chương trình:

PROCEDURE SelectionSort;

VAR i,j,LowIndex: integer;

LowKey: KeyTYPE;

BEGIN

{1} FOR i := 1 TO n-1 DO

BEGIN

{2} LowIndex := i;

{3} LowKey := a[i].key;

{4} FOR j := i+1 TO n DO

{5} IF a[j].key < LowKey THEN

BEGIN

{6} LowKey := a[j].key;

{7} LowIndex := j;

END;

{8} Swap(a[i],a[LowIndex]);

END;

END;

* Đánh giá độ phức tạp:

Các lệnh: {2}, {3}có thời gian thực hiện là O(1).

Vòng lặp FOR {4} – {7} thực hiện n-i lần vì j chạy từ i+1 đến n, mỗi lần lặp thực hiện hết

O(1) thời gian nên nó tốn O (n-i) thời gian. Mặc khác vòng lặp {1} thực hiện n-1 lần nên thời

gian tổng cộng để thực hiện chương trình là

Page 22: Chđề BD HS giỏi Tin 12

22

)(2

1)n(n1...2)(n1)(ni)(nT(n) 2

1n

1i

nO

c. Sắp xếp thêm dần (sắp xếp xen - Insertion Sort):

* Ý tưởng: Lần lượt xét các phần tử trong dãy, tại mỗi phần tử đang xét ta sẽ chèn vào vị trí

thích hợp trong danh sách các phần tử được xét trước đó.

* Mô tả cụ thể:

Trước hết ta xem phần tử a[1] là một dãy đã có thứ tự.

- Lượt 1, xen phần tử a[2] vào danh sách đã có thứ tự a[1] sao cho a[1], a[2] là một danh

sách có thứ tự.

- Lượt 2, xen phần tử a[3] vào danh sách đã có thứ tự a[1], a[2] sao cho a[1], a[2], a[3] là

một danh sách có thứ tự.

- Tổng quát, lượt i, xen phần tử a[i+1] vào danh sách đã có thứ tự a[1],a[2],..a[i] sao cho

a[1], a[2],.. a[i+1] là một danh sách có thứ tự.

- Phần tử đang xét a[j] sẽ được xen vào vị trí thích hợp trong danh sách các phần tử đã được

sắp trước đó a[1],a[2],..a[j-1] bằng cách so sánh khoá của a[j] với khoá của a[j-1] đứng ngay

trước nó. Nếu khoá của a[j] nhỏ hơn khoá của a[j-1] thì hoán đổi a[j-1] và a[j] cho nhau và tiếp

tục so sánh khoá của a[j-1] (lúc này a[j-1] chứa nội dung của a[j]) với khoá của a[j-2] đứng

ngay trước nó...

- Sau n-1 lượt thực hiện việc chèn thì mảng đã được sắp xếp.

Ví dụ: Sắp xếp mảng gồm 10 mẩu tin có khóa là các số nguyên: 5, 6, 2, 2, 10, 12, 9, 10, 9

và 3

Bảng sau ghi lại các giá trị khoá tương ứng với từng lượt thực hiện thuật toán trên.

Khóa

Lượt a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a[10]

Ban đầu 5 6 2 2 10 12 9 10 9 3

Lượt 1 5 6

Lượt 2 2 5 6

Lượt 3 2 2 5 6

Lượt 4 2 2 5 6 10

Lượt 5 2 2 5 6 10 12

Lượt 6 2 2 5 6 9 10 12

Lượt 7 2 2 5 6 9 10 10 12

Lượt 8 2 2 5 6 9 9 10 10 12

Lượt 9 2 2 3 5 6 9 9 10 10 12

Page 23: Chđề BD HS giỏi Tin 12

23

* Chương trình:

PROCEDURE InsertionSort;

VAR i,j: integer;

BEGIN

{1} FOR i := 2 TO n DO

BEGIN

{2} J := i;

{3} WHILE (j>1) AND (a[j].key < a[j-1].key) DO

BEGIN

{4} swap(a[j], a[j-1]);

{5} j := j-1;

END;

END;

END;

* Đánh giá độ phức tạp:

Vòng lặp {1} có i chạy từ 2 đến n nên nếu gọi T(n) là thời gian để sắp n phần tử thì ta có

)(2

1)n(ni)(nT(n) 2

1n

1i

nO

5. Thuật toán sắp xếp nhanh (QuickSort):

Chúng ta vẫn xét mảng a các phần tử a[1]..a[n]. Giả sử x là 1 giá trị khóa mà ta gọi là

“chốt” . Ta phân hoạch dãy a[1]..a[n] thành hai mảng con "bên trái" và "bên phải". Mảng con

"bên trái" bao gồm các phần tử có khóa nhỏ hơn chốt, mảng con "bên phải" bao gồm các phần

tử có khóa lớn hơn hoặc bằng chốt.

Sắp xếp mảng con “bên trái” và mảng con “bên phải” thì mảng đã cho sẽ được sắp bởi vì tất

cả các khóa trong mảng con “bên trái“ đều nhỏ hơn các khóa trong mảng con “bên phải”.

Việc sắp xếp các mảng con “bên trái” và “bên phải” cũng được tiến hành bằng phương pháp

nói trên.

Một mảng chỉ gồm một phần tử hoặc gồm nhiều phần tử có khóa bằng nhau thì đã có thứ tự.

Để sắp xếp một dãy có chỉ số từ L đến H ta tiến hành các bước như sau:

- Chọn chốt x (có thể chọn ngẫu nhiên hoặc định sẵn thường là chọn x=a[(L+H)div 2])

- Phân hoạch mảng đã cho thành hai mảng con a[L]..a[x] và a[x+1]..a[H]. So sánh các phẩn

tử thuộc mảng bên trái và bên phải với chốt. Cho i chạy từ L sang phải, j chạy từ H sang trái;

nếu phát hiện một cặp ngược thứ tự: i<=j và a[i].key>=a[j].key thì phải đổi chổ hai phần tử đó

cho đến khi i>j. Lúc đó dãy ở trình trạng: khóa các phần tử đoạn L..i < khóa của x; khóa của

các phần tử đoạn j..H >= khóa của x.

- Sắp xếp mảng a[L]..a[j]

- Sắp xếp mảng a[i]..a[H]

Thủ tục QuickSort(L, H) sau dùng để sắp xếp đoạn từ L tới H. Để sắp xếp dãy số, ta gọi

QuickSort(1,n).

Page 24: Chđề BD HS giỏi Tin 12

24

PROCEDURE QuickSort(L, H: longint);

VAR i,j : longint;

x, tmp: object;

BEGIN

i:=L;

j:=H;

x:=a[(L+H) div 2];

REPEAT

WHILE a[i].key < x.key DO inc(i);

WHILE a[j].key > x.key DO inc(j);

IF i<=j THEN

BEGIN

tmp:a[i];

a[i]:=a[j];

a[j]:=tmp;

inc(i);

dec(j);

END;

UNTIL i>j;

IF L<j THEN QuickSort(L,j);

IF i<H THEN QickSort(i, H);

END;

* Đánh giá độ phức tạp

QuickSort sẽ hoạt động rất kém trong trường hợp dãy đã được sắp sẵn. Khi đó phải mất đến

N lần gọi đệ quy và mỗi lần chỉ loại được 1 phần tử. Thời gian thực hiện trong trường hợp xấu

nhất này là khoảng N2/2 có nghĩa là O(N

2)

Trong trường hợp tốt nhất phân chia sẽ được hai nữa dãy bằng nhau. Khi đó thời gian thực

hiện của thuật toán T(N) sẽ được tính là

T(N) = 2T(N/2) + N

Hay T(N) ≈ NlogN nghĩa là O(NlogN)

Trong trường hợp trung bình thuật toán cũng có độ phức tạp khoảng 2NlogN = O(NlogN).

6. Thuật toán sắp xếp hòa nhập (trộn) hai đƣờng trực tiếp (MergeSort):

Ý tưởng của thuật toán này bắt nguồn từ việc trộn hai dãy đã được sắp xếp thành một dãy

mới cũng được sắp xếp.

a. Trộn hai dãy đã sắp xếp:

Bài toán: Cho hai mảng A và B đã được sắp xếp theo chiều không giảm. Từ hai mảng đã

cho hãy đưa các giá trị của A và B vào mảng C mà vẫn đảm bảo trật trự sắp xếp.

Giả sử ta có hai dãy đã sắp xếp như sau:

17 32 49 89 06 25 53

Page 25: Chđề BD HS giỏi Tin 12

25

Để giải quyết bài này ta dùng hai biến duyệt từ đầu mỗi dãy. Tại mỗi bước, tiến hành so

sánh giá trị của hai phần tử tại vị trí của hai biến duyệt. Nếu phần tử nào nhỏ hơn ta đưa

phần tử đó xuống dưới dãy mới và tăng biến duyệt tương ứng lên 1. Quá trình được lặp lại

cho tới khi tất cả các phần tử của hai dãy đã được duyệt và xét

Mô phỏng thuật toán của ví dụ trên:

Ban đầu:

Bước 1:

Bước 2:

Bước 3:

Bước 4:

Bước 5:

Bước 6:

Page 26: Chđề BD HS giỏi Tin 12

26

Bước 7:

Thủ tục trộn hai dãy đã sắp xếp trên HS tự viết.

Ta thấy rằng để thực hiện việc sắp xếp trộn thì hai dãy được trộn không phải là hai dãy

riêng biệt mà nằm trên cũng 1 dãy. Hãy nói cách khác ta trộn hai phần của một dãy chứ

không phải hai dãy riêng biệt. Ngoài ra kết quả trộn lại được lưu ngay tại dãy ban đầu chứ

không lưu ra một dãy khác.

Do vậy ta phải cải tiến thủ tục để trộn hai phần của một dãy A (với phần đầu có chỉ số từ

al đến am, phần sau có chỉ số từ am+1 đến ar). Dùng một mảng tạm C để lưu kết quả trộn

sau đó sao chép vào mảng ban đầu a.

Thủ tục này được viết như sau:

PROCEDURE Merger(VAR A: object; al,am,ar:longint);

VAR i, j, k: longint;

C:object;

BEGIN

i := al; j := am+1; k:= al;

WHILE (i<=am) and (j<=ar) DO

BEGIN

IF A[i]>A[j] THEN BEGIN C[k]:=A[j]; inc(j); END

ELSE BEGIN C[k]:=A[i]; inc(i); END;

inc(k);

END;

WHILE i<=am DO BEGIN C[k]:=A[i]; inc(i); inc(k); END;

WHILE j<=ar DO BEGIN C[k]:=A[j]; inc(j); inc(k); END;

FOR i:=al TO am DO A[i]:=C[i];

END;

b. Hòa nhập hai đường trược tiếp

Ý tưởng: Để thực hiện việc sắp xếp hòa nhập hai đường trực tiếp đầu tiên ta coi mỗi

phần tử của dãy là 1 danh sách con gồm 1 phần tử đã được sắp xếp. Tiếp theo trộn từng 2

cặp dãy con 1 phần tử kề nhau để tạo thành các dãy con 2 phần tử được sắp. Các dãy con 2

phần tử được sắp này lại được trộn với nhau tạo thành dãy con 4 phần tử được sắp. Quá

trình trên tiếp tục cho đến khi còn duy nhất 1 dãy con được sắp, đó chính là dãy ban đầu.

Page 27: Chđề BD HS giỏi Tin 12

27

Ví dụ mô phỏng việc sắp xếp 1 dãy bằng thuật toán trên

Ta sử dụng đệ quy để viết thủ tục sắp xếp trên

PROCEDURE MegerSort(VAR A:object; L, R:

longint);

VAR mid: longint;

BEGIN

IF R>L THEN

BEGIN

mid:= (L+R) div 2;

MegerSort(A, L, Mid);

MegerSort(A, Mid+1, R);

MegerSort(A, L, Mid, R);

END;

END;

BÀI TẬP

Câu 1. Cho mảng A[1..N, 1..M] chứa các số nguyên. Hãy sắp xếp lại các giá trị của các ô

trong A sao cho

* A[i,1] ≤ A[i,2] ≤ …≤ A[i,M]

* A[1,j] ≤ A[2,j] ≤…≤ A[N,j]

Dữ liệu: cho file MANG.INP gồm N+1 dòng

+ Dòng 1: chứa hai số N và M

+ Dòng i+1 (1 ≤ i ≤ N): ghi M số A[i,1], A[i,2], ..., A[i,N]

Các số ghi trên cùng một dòng cách nhau ít nhất một dấu cách.

Kết quả ghi vào file có tên MANG.OUT

Page 28: Chđề BD HS giỏi Tin 12

28

Ví dụ:

MANG.INP MANG.OUT

5 8

1 3 9 8 3 2 4 5

5 2 4 1 6 1 7 9

4 3 3 4 1 0 3 2

5 3 8 1 6 3 5 4

8 2 1 2 1 1 3 4

1 1 1 1 1 1 1 1

2 2 2 2 2 2 3 3

3 3 3 3 3 3 4 4

4 4 4 4 5 5 5 5

6 6 7 8 8 8 9 9

Câu 2. Xét tập F(N) tất cả các số hữu tỉ trong đoạn [0,1] với mẫu số không vượt quá N

(1<N<=100).

Ví dụ, tập F(5): 1

1

5

4

4

3

3

2

5

3

2

1

3

1

4

1

5

1

1

0

Cho trước một số nguyên dương N, viết chương trình in ra mọi phân số tối giản thuộc

F(N) theo thứ tự tăng dần của giá trị.

Câu 3. Cho N (N<=300) đoạn số nguyên [ai, bi] hãy tìm một số mà số đó thuộc nhiều đoạn

số nguyên nhất.

Ví dụ, có 5 đoạn [0,10], [2,3], [4,7], [3,5], [5,8], ta chọn số 5 thuộc 4 đoạn [0,10], [4,7],

[3,5], [5,8].

Câu 4. Cho 2 dãy số: A có n phần tử, B có m phần tử, các phần tử là các số nguyên x

Tìm số lượng giá trị trong A mà có mặt trong B

Yêu cầu: Viết số lượng tìm ra được

Dữ liệu vào: file AB.IN có dòng đầu là hai số n, m.

Từ dòng thứ 2 lần lượt là các số của A sau đó là các số của B. (0<n,m<10^6, |x|<10^9)

Kết quả ra: File AB.OUT gồm 1 dòng ghi số lượng tìm được

Ví dụ:

AB.IN AB.OUT

3 4

2 5 6 7 5 5 6

2

Câu 5. Để quản lí nhân sự ở một tỉnh nọ (có khoảng 1 triệu người), hãy sắp xếp tuổi của dân

cư ở đây theo thứ tự từ nhỏ đến lớn. Biết rằng tuổi dân cư chỉ nằm trong khoảng từ 1 đến

100.

Tuổi của dân cư được cho vào file T_IN.TXT viết liên tiếp nhau cách nhau ít nhất một

dấu cách hoặc một dấu xuống dòng. Kết quả đưa ra file T_OUT.TXT cấu trúc file

vào nhưng đã được sắp xếp.

Page 29: Chđề BD HS giỏi Tin 12

29

Ví dụ:

T_IN.TXT T_OUT.TXT

1 2 20 4 45 62 3

2 4 55 2 4 5 100

1 2 2 2 3 4 4 4 5

20 45 55 62 100

Câu 6. Người ta có N đoạn dây xích (N<=20000), mỗi đoạn dây xích là một chuỗi các mắt

xích được nối với nhau. Các đoạn dây xích này tách rời nhau. Mỗi đoạn có không quá 20000

mắt xích.

Bằng cách cắt ra một mắt xích, sau đó hàn lại, ta có thể nối hai dây xích thành một đoạn.

Thời gian để cắt và hàn mỗi mắt xích là 1 đơn vị thời gian và được xem là bằng nhau với

mọi mắt xích.

Nhiệm vụ của bạn là phải nối chúng lại thành một đoạn xích duy nhất với thời gian ít

nhất (hay số mắt xích bị cắt và hàn lại là ít nhất).

Input:

Dữ liệu cho trong file NOIXICH.IN có cấu trúc như sau:

Dòng đầu tiên là số N, số đoạn xích.

Những dòng tiếp theo ghi N số nguyên dương, số thứ i là số mắt xích

có trong đoạn xích thứ i (1<= i <= N)

Hai số cạnh nhau trên một dòng cách nhau ít nhất là một dấu cách.

Output:

Kết quả ghi vào file NOIXICH.OUT một số duy nhất là số đơn vị thời gian mà bạn cần

nối N đoạn xích đã cho.

Ví dụ:

NOIXICH.IN NOIXICH.OUT NOIXICH.IN NOIXICH.OUT

3

4 7

6

2 4

5 7 8

9

3

Câu 7. Cho đoạn thẳng [a0,b0] và một dãy các đoạn thẳng [a1,b1], [a2,b2], [a3,b3], …,

[an,bn]. Với 0<n<106, ai, bi (i= n0, ) là số nguyên và -10

9 <ai, bi <10

9. Tìm phần đoạn thẳng

riêng của đoạn thẳng [a0,b0] tức là các điểm thuộc [a,b] nhưng không thuộc bất cứ đoạn

[ai,bi] nào với i= n1,

Yêu cầu: Chỉ ra các phần đoạn thẳng riêng tìm được. Nếu không thông báo không tìm ra.

Dữ liệu vào: File DOAN.INP có các thông tin

- Dòng đầu tiên chứa hai số nguyên a0, b0 và số n

- Dòng thứ 2 chứa các cặp số nguyên dương ai, bi (i= n1, )

Page 30: Chđề BD HS giỏi Tin 12

30

Dữ liệu ra: File DOAN.OUT có các thông tin

- Dòng đầu tiên: ghi từ Yes hoặc No tùy theo có tồn tại đoạn riêng hay không

- Từ dòng thứ 2 trở đi ghi các đoạn riêng nếu có (mỗi đoạn riêng trên một dòng)

Ví dụ:

DOAN.INP DOAN.OUT

-3 5 4

-8 -2 8 20 -1 3 4 6

YES

-2 -1

3 4

Page 31: Chđề BD HS giỏi Tin 12

31

Chuyên đề:

PHƢƠNG PHÁP QUY HOẠCH ĐỘNG

I. GIỚI THIỆU

Trong chương trình Tin học chuyên ngành ở các trường đại học sư phạm, phương pháp

quy hoạch động hầu như không được nhắc đến hoặc chỉ giới thiệu sơ qua nên nhiều sinh

viên mới ra trường tỏ ra lúng túng khi gặp những bài toán dạng này trong việc giảng dạy

môn Tin học dành cho các lớp chuyên Tin ở phổ thông. Hơn nữa, tài liệu, sách báo viết về

phương pháp quy hoạch động rất ít, hầu hết giáo viên đều tự tìm tòi, nghiên cứu, tự biên

soạn chương trình để giảng dạy. Mấy năm gần đây Bộ Giáo dục và Đào tạo mới chính thức

đưa ra chương trình giảng dạy dành cho các lớp chuyên Tin ở các trường phổ thông, trong

đó quy hoạch động là một chuyên đề bắt buộc trong chương trình tin học chuyên của lớp 11

chiếm một thời lượng lớn (15 tiết). Những bài toán áp dụng phương pháp quy hoạch động

cũng thường xuyên xuất hiện trong các đề thi học sinh giỏi Tin học cấp tỉnh, cấp quốc gia,

Olympic cho nên việc nắm được kiến thức, kỹ năng giải bài toán quy hoạch động của giáo

viên dạy chuyên Tin cũng như học sinh trong các đội tuyển Tin học là vấn đề rất cần thiết.

Bản thân tôi là một giáo viên giảng dạy môn Tin học trong trường THPT chuyên Lê Quý

Đôn – Khánh Hoà, đã qua nhiều năm tham gia bồi dưỡng đội tuyển học sinh giỏi môn Tin

học ở cấp trường, cấp tỉnh và qua hai năm giảng dạy lớp chuyên Tin, tôi đã rút ra được một

số kinh nghiệm trong việc giảng dạy chuyên đề quy hoạch động. Sau đây tôi xin được giới

thiệu một số kinh nghiệm trong việc sử dụng phương pháp quy hoạch động để giải một số

bài toán trong tin học.

Quy hoạch động là một phương pháp nhằm giảm thời gian chạy của các thuật toán thể

hiện các tính chất của các bài toán con gối đầu nhau (overlapping subproblem) và cấu trúc

con tối ưu (optimal substructure). Phương pháp quy hoạch động do nhà toán học Richard

Bellman pháp minh vào năm 1953.

Phương pháp quy hoạch động (dynamic programming) là một kỹ thuật được áp dụng để

giải nhiều lớp bài toán, đặc biệt là các bài toán tối ưu. Đây là phương pháp có ý nghĩa trong

việc giải quyết các bài toán thực tế.

Phương pháp này dựa trên nguyên lý tối ưu của Bellman: Nếu một dãy các lựa chọn là

tối ưu thì mọi dãy con của nó cũng là tối ưu.

Ý tưởng của phương pháp: Để không phải tính lại kết quả bài toán con ta lưu các kết quả

đã tính vào một bảng và chỉ việc sử dụng lại giá trị của nó khi cần thiết.

Khác với phương pháp chia để trị là kỹ thuật tiếp cận TOP - DOWN (đi từ trên xuống),

phương pháp quy hoạch động dùng kỹ thuật BOTTOM - UP (đi từ dưới lên):

Xuất phát từ các trường hợp riêng đơn giản nhất, có thể tìm ngay ra nghiệm. Bằng cách

kết hợp nghiệm của chúng, ta nhận được nghiệm của bài toán cỡ lớn hơn. Cứ thế tiếp tục,

chúng ta sẽ nhận được nghiệm của bài toán ban đầu.

Page 32: Chđề BD HS giỏi Tin 12

32

Trong quá trình tìm nghiệm của bài toán từ dưới lên chúng ta sẽ sử dụng bảng để lưu giữ

kết quả của các bài toán con đã giải trước đó. Kết quả của những bài toán con này có thể là

cơ sở cho việc giải bài toán lớn hơn.

Khi giải một bài toán con, cần đến nghiệm của bài toán con nhỏ hơn, ta chỉ cần lấy kết

quả từ bảng, không cần phải giải lại. Chính vì thế mà giải thuật nhận được bằng phương

pháp này rất có hiệu quả.

Ƣu điểm của phƣơng pháp quy hoạch động: chương trình chạy nhanh.

Phạm vi áp dụng của phƣơng pháp quy hoạch động:

+ Các bài toán tối ưu: như tìm xâu con chung dài nhất, bài toán ba lô, tìm đường đi ngắn

nhất, bài toán Ôtômat với số phép biến đổi ít nhất, …

+ Các bài toán có công thức truy hồi.

Hạn chế của phƣơng pháp quy hoạch động:

Phương pháp quy hoạch động không đem lại hiệu quả trong các trường hợp sau:

+ Sự kết hợp lời giải của các bài toán con chưa chắc cho ta lời giải của bài toán lớn.

+ Số lượng các bài toán con cần giải quyết và lưu trữ kết quả có thể rất lớn, không thể

chấp nhận được.

+ Không tìm được công thức truy hồi.

II. CẤU TRÚC CHUNG CỦA CHƢƠNG TRÌNH CHÍNH:

BEGIN {Chương trình chính}

Chuẩn bị: đọc dữ liệu và khởi gán một số giá trị ban đầu;

Tạo bảng;

Tra bảng và ghi kết quả;

END.

III. CÁC BƢỚC ĐỂ GIẢI BÀI TOÁN QUY HOẠCH ĐỘNG:

1. Mô tả bài toán dưới dạng các bài toán con;

2. Tính nghiệm tối ưu của bài toán con trong trường hợp riêng đơn giản nhất;

3. Tìm các công thức đệ quy biểu diễn nghiệm tối ưu của bài toán lớn thông qua nghiệm

tối ưu của các bài toán con.

4. Tính nghiệm tối ưu từ dưới lên (bottom up) và ghi lại các nghiệm tối ưu của các bài

toán vào bảng.

5. Dựa vào bảng lưu kết quả của các bài toán đã tìm được để tìm nghiệm cho bài toán

ban đầu.

IV. MỘT SỐ VÍ DỤ

1. Dãy Fibonaci:

Đề bài: In ra màn hình 20 số hạng đầu của dãy Fibonaci.

Page 33: Chđề BD HS giỏi Tin 12

33

Biết: F1 = 1 ; F2 = 1

Fi = Fi-1+ Fi-2 với i > 2

Giải thuật:

+ Tính nghiệm của bài toán trong trường hợp riêng đơn giản nhất.

F1 = F2 = 1

+ Tìm các công thức đệ quy biểu diễn nghiệm tối ưu của bài toán lớn thông qua nghiệm

tối ưu của các bài toán con.

Fi = Fi-1+ Fi-2 với i > 2

Mười số hạng đầu của dãy Fibonaci được biểu diễn trong bảng sau:

i 1 2 3 4 5 6 7 8 9 10

F[i] 1 1 2 3 5 8 13 21 34 55

2. Tổ hợp chập k của n phần tử:

Đề bài: Tính các phần tử của mảng C[n, k] = k

nC = số tổ hợp chập k của n phần tử, với

0 ≤ k ≤ n ≤ 20.

Biết 10 n

nn CC

k

n

k

n

k

n CCC 1

1

1

Giải thuật:

+ Tính nghiệm của bài toán trong trường hợp riêng đơn giản nhất:

FOR i := 1 TO n DO

BEGIN

C[0, i] := 1;

C[i, i] := 1;

END;

+ Tìm các công thức đệ quy biểu diễn nghiệm tối ưu của bài toán lớn thông qua nghiệm

tối ưu của các bài toán con.

FOR i := 2 To n Do

FOR j := 1 To i-1 Do C[i, j] := C[i-1,j-1] + C[i-1,j];

n\k 0 1 2 3 4 5

1 1 1

2 1 2 1

3 1 3 3 1

4 1 4 6 4 1

5 1 5 10 10 5 1

Page 34: Chđề BD HS giỏi Tin 12

34

3. Dãy con không giảm liên tục dài nhất

Đề bài: Cho dãy số nguyên A1, A2, ... , AN. Hãy tìm dãy con B gồm một số phần tử liên

tục trong A không giảm dài nhất.

Ví dụ:

Input

9

3 2 1 4 5 8 9 6 7

Output

1 4 5 8 9

Giải thuật:

+ Gọi T[i] (i=1..N) là độ dài lớn nhất của dãy con không giảm liên tục khi có i phần tử từ

1 đến i trong A để chọn, trong đó phần tử thứ i phải được chọn.

+ Trường hợp đơn giản nhất giả sử dãy A chỉ có 1 phần tử thì T[1]=1;

+ Công thức đệ quy:

Với i ≥ 2 ta có:

Nếu A[i] ≥ A[i-1] thì T[i] = T[i-1] + 1, ngược lại T[i] = 1.

FOR i:=2 to N do

If A[i] >= a[i-1] then T[i]:=T[i-1] + 1 Else T[i]:=1;

+ Nghiệm của bài toán:

Tìm v là vị trí phần tử lớn nhất trong mảng T

v:=1;

FOR i:=1 to N do if T[i] ≥ T[v] then v:=i;

Như vậy dãy con tìm được sẽ có T[v] phần tử và phần tử cuối cùng là phần tử thứ v

trong A. Cho nên dãy con tìm được sẽ là dãy gồm các phần tử từ v – T[v] + 1 đến phần tử

thứ v của dãy A.

In dãy con tìm được

FOR i:= v-T[v]+1 to v do write(A[i], ‘ ‘);

4. Tìm dãy con không giảm dài nhất:

Đề bài: Cho một dãy n số nguyên. Hãy loại bỏ khỏi dãy một số phần tử để được một dãy

con không giảm dài nhất. In ra dãy con đó.

Ví dụ:

Input:

10

Page 35: Chđề BD HS giỏi Tin 12

35

2 6 -7 5 8 1 -3 5 15 9

Kết quả tìm được dãy con không giảm dài nhất có 4 phần tử:

-7 -3 5 9

Giải thuật:

+ Tổ chức dữ liệu:

Gọi A là dãy ban đầu.

Gọi B[i] là số phần tử của dãy con dài nhất trong dãy có i phần tử đầu tiên A[1] .. A[i] và

A[i] được chọn làm phần tử cuối. (i [1, n])

C là dãy con không giảm dài nhất tìm được.

Truoc[i] là chỉ số của phần tử trước phần tử i (các phần tử giữ lại C).

+ Giải thuật tạo bảng: (Tính mảng B và mảng Truoc)

Trường hợp đơn giản nhất: dãy chỉ có 1 phần tử, thì B[1] := 1;

FOR i = 2 to n Do

Với mọi (j < i) và (A[j] <= A[i]), tìm B[j] lớn nhất (gọi là BMax).

B[i] := Bmax + 1;

Trước[i] := j; {j là chỉ số ứng với BMax tìm được}

Trong ví dụ trên ta có bảng sau:

i 1 2 3 4 5 6 7 8 9 10

A[i] 2 6 -7 5 8 1 -3 5 15 9

B[i] 1 2 1 2 3 2 2 3 4 4

Truoc[i] 0 1 0 3 4 3 3 7 8 8

Dãy con không giảm dài nhất có 4 phần tử: -7 -3 5 9

PROCEDURE TaoBang;

VAR i, j, BMax, chiSo :byte;

BEGIN

B[1] := 1; truoc[1]:=0;

FOR i := 2 TO n DO

BEGIN

BMax := 0; chiso:=0;

FOR j := i-1 DOwnTO 1 DO

IF (A[j] <= A[i]) and (B[j] > BMax) THEN

BEGIN

BMax := B[j];

chiSo := j;

END;

B[i] := BMax + 1; Truoc[i] := chiSo;

END;

END;

Page 36: Chđề BD HS giỏi Tin 12

36

+ Tra bảng: để tìm các phần tử của dãy C:

Tìm phần tử lớn nhất của mảng B. (ứng với chỉ số ChiSoMax). Phần tử lớn nhất của

mảng B chính là số phần tử của dãy C.

A[ChiSoMax] là phần tử cuối của dãy C. Dựa vào mảng Truoc, ta tìm các phần tử

còn lại trong dãy C: tìm ngược từ cuối dãy lên đầu dãy.

PROCEDURE TraBang;

VAR chiSo, ChiSoMax, i : byte;

BEGIN

ChiSoMax := n;

FOR i:= n-1 DOwnTO 1 DO

IF B[i] > B[ChiSoMax] THEN ChiSoMax := i;

chiSo := ChiSoMax;

FOR i := B[ChiSoMax] DOwnTO 1 DO

BEGIN

C[i]:= A[chiSo];

chiSo := Truoc[chiSo];

END;

END;

5. Bài toán balô 1:

Đề bài: Cho n món hàng (n ≤ 50). Món thứ i có khối lượng là A[i] (số nguyên). Cần

chọn những món hàng nào để bỏ vào một ba lô sao tổng khối lượng của các món hàng đã

chọn là lớn nhất nhưng không vượt quá khối lượng W cho trước. (W ≤ 100). Mỗi món chỉ

chọn 1 hoặc không chọn.

Input:

n W

A[1] A[2] … A[n]

Ví dụ:

4 10

5 2 4 3

Output:

Tổng khối lượng của các món hàng bỏ vào ba lô.

Khối lượng của các món hàng đã chọn.

Trong ví dụ trên:

Tổng khối lượng của các món hàng bỏ vào ba lô là 10

Khối lượng các món hàng được chọn: 5 2 3

Hƣớng giải:

Page 37: Chđề BD HS giỏi Tin 12

37

+ Tổ chức dữ liệu:

Fx[k, v] là tổng khối lượng của các món hàng bỏ vào ba lô khi có k món hàng đầu tiên

để chọn và khối lượng tối đa của ba lô là v.

Với k ϵ [1, n], v ϵ [1, W].

Nói cách khác: Khi có k món để chọn, Fx[k, v] là khối lượng tối ưu khi khối lượng tối đa

của ba lô là v.

Khối lượng tối ưu luôn nhỏ hơn hoặc bằng khối lượng tối đa:

Fx[k,v] ≤ v.

Ví dụ: Fx[4, 10] = 8 Nghĩa là trong trường hợp tối ưu, tổng khối lượng của các món

hàng được chọn là 8, khi có 4 món đầu tiên để chọn (từ món thứ 1 đến món thứ 4) và khối

lượng tối đa của ba lô là 10. Không nhất thiết cả 4 món đều được chọn.

+ Giải thuật tạo bảng:

Trường hợp đơn giản chỉ có 1 món để chọn: Ta tính Fx[1, v] với mọi v.

Nếu có thể chọn (nghĩa là khối lượng tối đa của ba lô >= khối lượng của các món hàng

thứ 1), thì chọn: Fx[1, v] := A[1];

Ngược lại ( v < A[1] ), không thể chọn, nghĩa là Fx[1, v] := 0;

* Giả sử ta đã tính được Fx[k–1 , v ] đến dòng k–1, ∀ v ∈ [1, W]. Khi có thêm món thứ k

để chọn, ta cần tính Fx[k , v] ở dòng k, ∀ v ∈ [1,W].

Nếu có thể chọn món hàng thứ k (v >= A[k]), thì có 2 trường hợp:

– Trường hợp 1: Nếu chọn thêm món thứ k bỏ vào ba lô thì:

Fx[k, v] := Fx[k–1, u ] + A[k]; Với u là khối lượng còn lại sau khi chọn món thứ k.

u = v – A[k]

– Trường hợp 2: Ngược lại, không chọn món thứ k, thì

Fx[k, v] := Fx[k–1, v ];

Trong 2 trường hợp trên ta chọn trường hợp nào có Fx[k, v] lớn hơn. Ngược lại (v <

A[k]), thì không thể chọn, nghĩa là Fx[k, v] := Fx[k–1, v];

+ Công thức đệ quy là:

If v >= A[k] Then Fx[k,v] := Max(Fx[k-1, v - A[k]] + A[k] , Fx[k-1,v])

Else Fx[k,v] := Fx[k-1, v];

Dưới đây là bảng Fx[k,v] tính được trong ví dụ trên:

A k\v 1 2 3 4 5 6 7 8 9 10

5 1 0 0 0 0 5 5 5 5 5 5

2 2 0 2 2 2 5 5 7 7 7 7

4 3 0 2 2 4 5 6 7 7 9 9

3 4 0 2 3 4 5 6 7 8 9 10

Page 38: Chđề BD HS giỏi Tin 12

38

PROCEDURE TaoBang;

VAR k ,v : integer;

BEGIN

FOR v:=1 TO W DO

IF v >= A[1] THEN Fx[1, v] := A[1] ELSE Fx[1, v] := 0;

FOR k:= 2 TO n DO FOR v:=1 TO W DO

IF v >= A[k] THEN

Fx[k,v]:= Max(Fx[k-1,v-A[k]]+ A[k], Fx[k-1,v])

ELSE Fx[k,v]:=Fx[k-1,v];

END;

+ Giải thuật tra bảng để tìm các món hàng đƣợc chọn:

Chú ý: Nếu Fx[k, v] = Fx[k–1, v] thì món thứ k không được chọn.

Fx[n, W] là tổng khối lượng tối ưu của các món hàng bỏ vào ba lô.

Bƣớc 1: Bắt đầu từ k = n, v = W.

Bƣớc 2: Tìm trong cột v, ngược từ dưới lên, ta tìm dòng k sao cho Fx[k,v] > Fx[k–1, v].

Đánh dấu món thứ k được chọn:

Chọn[k] := true;

Bƣớc 3: v := Fx[k, v] – A[k].

Nếu v > 0 thì thực hiện bước 2, ngược lại thực hiện bước 4

Bƣớc 4: Dựa vào mảng Chọn để in ra các món hàng được chọn.

PROCEDURE TraBang;

VAR k, v: Integer;

BEGIN

k := n; v := w;

FillChar(chon,SizeOf(chon),false);

REPEAT

WHILE Fx[k,v] = Fx[k-1,v] DO Dec(k);

chon[k]:= True;

v := Fx[k,v] - A[k];

UNTIL v = 0;

FOR k := 1 TO n DO

IF chon[k] THEN Write(A[k]:5); Writeln;

END;

6. Bài toán ba lô 2

Đề bài: Cho n món hàng (n ≤ 50). Món thứ i có khối lượng là A[i] và giá trị C[i] (số

nguyên). Cần chọn những món hàng nào để bỏ vào một ba lô sao tổng giá trị của các món

Page 39: Chđề BD HS giỏi Tin 12

39

hàng đã chọn là lớn nhất nhưng tổng khối lượng của chúng không vượt quá khối lượng W

cho trước (W ≤ 100). Mỗi món chỉ chọn 1 hoặc không chọn.

Input:

n W

A[1] C[1]

A[2] C[2]

A[n] C[n]

Ví dụ:

5 13

3 4

4 5

5 6

2 3

1 1

Output:

Tổng giá trị của các món hàng bỏ vào ba lô.

Khối lượng và giá trị của các món hàng đã chọn.

Trong ví dụ trên:

Tổng giá trị của các món hàng bỏ vào ba : 16

Các món được chọn:

1(3, 4) 2(4, 5) 3(5, 6) 5(1, 1)

Hƣớng dẫn giải:

Tương tự bài ba lô 1, nhưng Fx[k, v] là giá trị lớn nhất của ba lô khi có k món hàng đầu

tiên để chọn và khối lượng tối đa của ba lô là v.

Công thức đệ quy là:

If v >= A[k] Then

Fx[k,v]:= Max(Fx[k-1, v-A[k]] + C[k], Fx[k-1,v])

Else

Fx[k,v]:=Fx[k–1,v];

Chú ý: chỉ khác bài balô 1 ở chỗ dùng C[k] thay cho A[k]

Page 40: Chđề BD HS giỏi Tin 12

40

Dưới đây là bảng Fx[k,v] tính được trong ví dụ trên:

k/v 1 2 3 4 5 6 7 8 9 10 11 12 13

1 0 0 4 4 4 4 4 4 4 4 4 4 4

2 0 0 4 5 5 5 9 9 9 9 9 9 9

3 0 0 4 5 6 6 9 10 11 11 11 15 15

4 0 3 4 5 7 8 9 10 12 13 14 15 15

5 1 3 4 5 7 8 9 10 12 13 14 15 16

7. Bài toán ba lô 3

Đề bài: Cho n loại hàng (n ≤ 50). Mỗi món hàng thuộc loại thứ i có khối lượng là A[i] và

giá trị C[i] (số nguyên). Số lượng các món hàng của mỗi loại không hạn chế. Cần chọn những

món hàng của những loại hàng nào để bỏ vào một ba lô sao tổng giá trị của các món hàng đã

chọn là lớn nhất nhưng tổng khối lượng của chúng không lượt quá khối lượng W cho trước

(W ≤ 100). Mỗi loại hàng có thể hoặc không chọn món nào, hoặc chọn 1 món, hoặc chọn

nhiều món.

Input:

n W

A[1] C[1]

A[2] C[2]

A[n] C[n]

Ví dụ:

5 13

3 4

4 5

5 6

2 3

1 1

Output:

Tổng giá trị của các món hàng bỏ vào ba lô.

Số lượng của các loại hàng đã chọn.

Trong ví dụ trên:

Tổng giá trị của các món hàng bỏ vào ba lô: 19

Các món được chọn:

Chọn 1 món hàng loại 1, mỗi món có khối lượng là 3 và giá trị là 4

Page 41: Chđề BD HS giỏi Tin 12

41

Chọn 5 món hàng loại 4, mỗi món có khối lượng là 2 và giá trị là 3

Hƣớng dẫn giải:

+ Tổ chức dữ liệu:

Fx[k, v] là tổng giá trị của các món hàng bỏ vào ba lô khi có k loại hàng

đầu tiên để chọn và khối lượng tối đa của ba lô là v.

Với k [1, n], v ∈ [1, W].

X[k, v] là số lượng các món hàng loại k được chọn khi khối lượng tối đa của

ba lô là v.

+ Giải thuật tạo bảng:

* Trường hợp đơn giản chỉ có 1 món để chọn: Ta tính Fx[1, v] với mọi v:

X[1, v] = v div A[1]

Fx[1, v] = X[1, v] * C[1]

* Giả sử ta đã tính được Fx[k–1 , v ] đến dòng k–1, với mọi v ∈ [1,W].

Khi có thêm loại thứ k để chọn, ta cần tính Fx[k , v] ở dòng k, với mọi v ∈ [1,W]

Nếu ta chọn xk món hàng loại k, thì khối lượng còn lại của ba lô dành cho các loại hàng từ

loại 1 đến loại k – 1 là: u = v – xk * A[k]

Khi đó giá trị của ba lô là: Fx[k, v]= Fx[k–1,u] + xk * C[k]

Với xk thay đổi từ 0 đến yk, ta chọn giá trị lớn nhất và lưu vào Fx[k, v].

Trong đó yk = v div A[k] là số lượng lớn nhất các món hàng loại k có thể được chọn bỏ

vào ba lô, khi khối lượng tối đa của ba lô là v.

Tóm lại: công thức đệ quy là:

Fx[k,v] = Max(Fx[k-1, v – xk * A[k]] + xk * C[k])

Max xét với xk thay đổi từ 0 đến v div A[k], và v – xk * A[k] > 0

Dưới đây là bảng Fx[k,v] và X[k, v] tính được trong ví dụ trên. Bảng màu xám là X[k, v]:

k/v 1 2 3 4 5 6 7 8 9 10 11 12 13

1 0 0 0 0 4 1 4 1 4 1 8 2 8 2 8 2 12 3 12 3 12 3 16 4 16 4

2 0 0 0 0 4 0 4 0 5 1 8 0 9 1 9 1 12 0 13 1 14 2 16 0 17 1

3 0 0 0 0 4 0 4 0 5 0 8 0 9 0 10 1 12 0 13 0 14 0 16 0 17 0

4 0 0 0 0 4 0 4 0 7 1 8 0 10 2 11 1 13 3 14 2 16 4 17 3 19 5

5 0 0 1 1 4 0 5 1 7 0 8 0 10 0 11 0 13 0 14 0 16 0 17 0 19 0

Page 42: Chđề BD HS giỏi Tin 12

42

PROCEDURE TaoBang;

VAR xk, yk, k: Byte;

FMax, XMax, v : Word;

BEGIN

FOR v:= 1 TO W DO

BEGIN

X[1, v] := v div A[1];

F[1, v] := X[1, v] * C[1];

END;

FOR k:= 2 TO n DO

FOR v:= 1 TO W DO

BEGIN

FMax := F[k-1, v] ;

XMax := 0;

yk := v div A[k];

FOR xk:= 1 TO yk DO

IF(v-xk*A[k]>0)andF[k-1,v-xk*A[k]]+xk*C[k]>FMax) THEN

BEGIN

FMax := F[k-1, v - xk * A[k]] + xk * C[k];

XMax:= xk;

END;

F[k, v] := FMax;

X[k, v] := XMax;

END;

END;

Giải thuật tra bảng:

Fx[n, W] là giá trị lớn nhất của ba lô.

Bắt đầu từ X[n, W] là số món hàng loại k được chọn.

Tính v = W – X[n, W]* A[n].

Tìm đến ô [n – 1, v ] ta tìm được X[n – 1, v]. Cứ tiếp tục ta tìm được X[1, v].

Chú ý: khi tra bảng, ta không dùng mảng Fx[k, v], nên ta có thể cải tiến: dùng 2 mảng

một chiều thay cho mảng hai chiều Fx.

8. Bài toán xâu con chung dài nhất

Đề bài: Cho hai xâu kí tự A và B. Tìm xâu kí tự C có nhiều kí tự nhất, với C vừa là xâu

con của xâu A, vừa là xâu con của xâu B. (Xâu con là xâu kí tự có được khi bỏ bớt một số kí

tự trong xâu cha).

Dƣ liệu: Vào từ tập tin văn bản XauChung.inp gồm hai dòng, mỗi dòng là một xâu kí

tự.

Kết quả: Đưa ra tập tin văn bản XauChung.out gồm 2 dòng:

• Dòng đầu là độ dài của xâu con chung dài nhất.

• Dòng thứ hai là xâu con chung C.

Page 43: Chđề BD HS giỏi Tin 12

43

Ví dụ

XauChung.INP XauChung.OUT

CEACEEC

AECECA

4

AEEC

Hƣớng dẫn:

+ Tổ chức dữ liệu

Mảng hai chiều L[0..Max, 0..Max] để lưu kết quả các bài toán con.

+ Tạo bảng

Gọi L[i,j] là độ dài lớn nhất của dãy con chung của hai dãy khi dãy A có i phần tử và dãy

B có j phần tử để chọn A1...Ai và B1..Bj. Với i<=m và j<=n (m,n là độ dài hai xâu).

+ Trƣờng hợp đơn giản: nếu i=0 hoặc j=0 (một trong hai xâu rỗng) thì L[i,j] =0.

+ Công thức đệ quy

Nếu (i>0) and (j>0) and (Ai<>Bj)

thì có L[i,j] =Max(L[i-1,j], L[i,j-1])

Nếu (i>0) and (j>0) and (Ai=Bj) thì L[i,j] = 1+ L[i-1,j-1]

+ Tạo bảng

PROCEDURE Taobang;

VAR i,j:byte;

BEGIN

Fillchar(L,sizeof(L),0);

FOR i:=1 TO m DO

FOR j:=1 TO n DO

IF A[i]=B[j] THEN L[i,j]:= 1 + L[i-1,j-1]

ELSE L[i,j]:=Max(L[i-1,j],L[i,j-1]);

END;

+ Tra bảng

Xâu chung C:= „‟

Xuất phát từ ô L[n,m] hướng về ô L[0,0].

Nếu A[i]=B[j] chọn A[i] hoặc B[j] đưa vào đầu dãy C rồi lùi về ô L[i-1,j-1]

Nếu Ai<>Bj thì:

Nếu L[i-1,j] > L[i,j-1] lùi về L[i-1,j]

Nếu L[i-1,j] <= L[i,j-1] thì lùi về L[i,j-1]

Page 44: Chđề BD HS giỏi Tin 12

44

PROCEDURE Trabang;

VAR i, j:byte;

BEGIN

C:=’’;

I:=m; j:=n;

REPEAT

IF A[i]=B[j] THEN

BEGIN

C:= A[i]+C;

Dec(i); Dec(j);

END

ELSE IF L[i,j-1] > L[i-1,j] THEN j:=j-1 ELSE i:=i-1

UNTIL (i=0) or (j=0);

END;

9. Bài toán chia kẹo

Đề bài: Cho n gói kẹo (n ≤ 50). Gói thứ i có A[i] viên kẹo. Cần chia các gói kẹo này cho

2 em bé sao cho tổng số viên kẹo mỗi em nhận được chênh lệch ít nhất. Mỗi em nhận

nguyên gói. Không mở gói kẹo ra để chia lại. Hãy liệt kê số kẹo trong các gói kẹo mỗi em

nhận được.

Input:

n

A[1] A[2] … A[n]

Output: Số kẹo trong các gói kẹo mỗi em nhận được, và tổng số kẹo mỗi em nhận được.

Hƣớng dẫn giải:

Gọi S là tổng số viện kẹo S := A[1] + A[2] + … + A[n];

S2 là nửa tổng số kẹo: S2 := S div 2;

Cho em bé thứ nhất chọn trước những gói kẹo sao cho tổng số viên kẹo mà em nhận

được là lớn nhất nhưng không vượt quá số kẹo S2.

Gói kẹo nào em bé thứ nhất không chọn thì em bé thứ hai chọn.

Bài toán được đưa về Bài toán ba lô 1(đã hướng dẫn ở trên).

10. Bài toán đổi tiền

Đề bài: Cho n loại tờ giấy bạc. Tờ giấy bạc thứ i có mệnh giá A[i]. Số tờ mỗi loại không

giới hạn. Cần chi trả cho khách hàng số tiền M đồng. Hãy cho biết mỗi loại tiền cần bao

nhiêu tờ sao cho tổng số tờ là ít nhất. Nếu không đổi được, thì thông báo “KHONG DOI

DUOC”. N < 50; A[i] < 256; M < 10000.

Input:

n M

A[1] A[2] … A[n]

Page 45: Chđề BD HS giỏi Tin 12

45

Ví dụ:

3 18

3 10 12

Output: Tổng số tờ phải trả. Số tờ mỗi loại.

Hƣớng dẫn giải: Tương tự Bài toán ba lô 3

Gọi Fx[i, j ] là số tờ ít nhất được dùng để trả số tiền j đồng khi có i loại tiền từ loại 1 đến

loại i. Với i = 1 .. n; j = 1 .. M.

X[i, j] là số tờ giấy bạc loại thứ i được dùng chi trả số tiền j đồng.

* Trường hợp đơn giản chỉ có 1 loại tiền để chọn: Ta tính Fx[1, j] với mọi j

F[1,j] = j div A[1] nếu j mod A[1] = 0

F[1,j] = ∞ nếu j mod A[1] ≠ 0 (không đổi được)

* Giả sử ta đã tính được Fx[i–1 , j ] đến dòng i–1, với mọi j ∈ [1,M]. Khi có thêm loại

tiền thứ i để chọn, ta cần tính Fx[i , j] ở dòng i, với mọi j ∈ [1, M].

Nếu ta chọn k tờ loại i, thì số tiền còn lại dành cho các loại tiền khác từ loại 1 đến loại i –

1 là: u = j – k * A[k]

Khi đó tổng số tờ là: Fx[i, j]= Fx[i–1,u] + k

Với k thay đổi từ 0 đến kMax, ta chọn giá trị nhỏ nhất và lưu vào Fx[i, j].

Trong đó kMax = j div A[k] là số tờ nhiều nhất của loại tiền i để đổi số tiền j.

Tóm lại: công thức đệ quy là:

Fx[i,j] = Min(Fx[i-1, j – k * A[i]] + k)

Min xét với k thay đổi từ 0 đến j div A[i], và j – k * A[i] > 0

+

V. BÀI TẬP

Bài 1 : Túi ba gang

Trong truyện cổ tích "Cây Khế" ta đã biết rằng chim thần chở người anh với một cái túi

ba gang đến hòn đảo đầy vàng bạc châu báu. Người em băn khoăn không biết chọn đồ vật

nào cho vào túi vì chỉ có một cái túi ba gang..

Giả sử rằng trên hòn đảo kia có N đồ vật khác nhau, đồ vật thứ i có giá trị là ai và có thể

tích là bi. Cũng giả sử rằng cái túi mà người em mang đi chỉ có thể tích là M. Bạn hãy giúp

người em chọn ra trong N đồ vật trên một số đồ vật sao cho tổng thể tích của các đồ vật

được chọn không vượt quá M và tổng giá trị các đồ vật được chọn là lớn nhất.

Input: Cho trong file văn bản CAYKHE.INP

Dòng đầu tiên ghi hai số N, M (N,M≤100)

N dòng tiếp theo, dòng thứ i ghi hai số ai và bi lần lượt là giá trị và thể tích của đồ vật

thứ i (ai, bi≤100)

Page 46: Chđề BD HS giỏi Tin 12

46

Output: Ghi ra file văn bản CAYKHE.OUT

Dòng đầu tiên ghi tổng giá trị lớn nhất có thể cho vào trong túi

Dòng thứ hai ghi số hiệu các đồ vật được cho vào trong túi. Đầu tiên ghi K là số lượng

đồ vật được chọn, tiếp theo là K số thể hiện số hiệu các đồ vật được chọn.

Ví dụ:

CAYKHE.INP CAYKHE.OUT

5 10

20 3

19 1

30 7

24 3

15 6

63

3 1 2 4

Bài 2: Kinh doanh bất động sản.

Tại thành phố Silicon, Một người nọ được thừa kế một khoản tiền N ngàn USD, người

đó quyết định đầu tư vào việc kinh doanh bất động sản bằng cách mua các mảnh đất hình

vuông có kích thước là các số nguyên, biết rằng mỗi mét vuông đất có giá trị 1 ngàn USD.

Hãy chỉ cách cho người nọ mua đất sao cho tổng số tiền mua đất đúng bằng N ngàn USD và

số mảnh đất mua được càng ít càng tốt.

Dữ liệu: Vào từ file văn bản MUADAT.INP gồm một số nguyên dương duy nhất N có giá

trị không vượt quá 60000

Kết quả: Ghi ra file văn bản MUADAT.OUT một dãy số nguyên dương xếp theo thứ tự

giảm dần là kích thước các mảnh đất mua được

Ví dụ:

MUADAT.INP MUADAT.OUT

30 4 3 2 1

Bài 3: Xây tháp

Có N khối đá hình hộp chữ nhật. Người ta muốn xây một cái tháp bằng cách chồng các

khối đá này lên nhau. Để đảm bảo an toàn, các khối đá được đặt theo nguyên tắc:

+ Chiều cao của mỗi khối là kích thước nhỏ nhất trong ba kích thước

+ Các mép của các khối đươc đặt song song với nhau sao cho không có phần nào

của khối nằm trên bị chìa ra ngoài so với khối nằm dưới.

Hãy tìm phương án xây dựng để tháp đạt được độ cao nhất.

Dữ liệu vào: Cho trong file TOWN.INP:

Dòng đầu tiên là số N

N dòng tiếp, mỗi dòng ghi 3 số nguyên dương là kích thước của một khối đá. Các

khối đá đươc đánh số từ 1 theo trình tự xuất hiện trong file.

Page 47: Chđề BD HS giỏi Tin 12

47

Kết quả: ghi ra file TOWN.OUT:

Dòng thứ nhất ghi số M là số lượng khối đá dùng để xây tháp

M dòng tiếp theo ghi các khối xếp từ đáy tháp lên đỉnh tháp, mỗi dòng gồm 4 số theo

thứ tự K a b c, trong đó K là số hiệu khối đá, a là kích thước chọn làm đáy nhỏ, b là

kích thước chọn làm đáy lớn, c là kích thước chọn làm chiều cao.

Các số trên một dòng trong các file được ghi cách nhau ít nhất một dấu cách. Giới hạn số

khối đá không quá 5000 và các kích thước của các khối đá không quá 255.

Ví dụ:

TOWN.INP TOWN.OUT

9

7 5 5

4 4 8

1 1 5

4 2 2

5 1 5

4 2 7

2 9 2

1 3 3

5 5 5

4

1 5 7 5

9 5 5 5

5 5 5 1

4 2 4 2

Bài 4: Vòng quanh thế giới (Đề thi học sinh giỏi Toàn quốc 2003)

Trên tuyến đường của xe du lịch vòng quanh thế giới xuất phát từ bến xe x có N khách

sạn đánh số từ 1 đến N theo thứ tự xuất hiện trên tuyến đường, trong đó khách sạn i cách địa

điểm xuất phát A[i] km (i=1,2,...,N): A[1]<A[2]<....<A[n].

Để đảm bảo sức khỏe cho hành khách, theo tính toán của các nhà chuyên môn, sau khi

đã chạy được P km xe nên dừng lại cho khách nghỉ ngơi ở khách sạn. Vì thế, nếu xe dừng

lại cho khách nghỉ ở khách sạn sau khi đã đi được Q km thì lái xe phải trả một lượng phạt là

(Q-P)2.

Yêu cầu: Hãy xác định xem trên tuyến đường đến khách sạn N, xe cần dừng lại nghỉ ở

những khách sạn nào để tổng lượng phạt mà lái xe phải trả là ít nhất.

Dữ liệu vào từ file văn bản TOURISM.INP

- Dòng đầu tiên chứa số nguyên dương N (N10000)

- Dòng thứ hai chứa số nguyên dương P (P500)

- Dòng thứ ba chứa các số nguyên dương A[1]], A[2], ..., A[n] (hai số liên tiếp trên

dòng được ghi cách nhau bởi dấu cách; A[i]2000000, i=1,2,...,N)

Kết quả ghi ra file văn bản TOURISM.OUT:

- Dòng đầu ghi Z là lượng phạt mà lái xe phải trả

- Dòng thứ 2 ghi k là tổng số khách sạn mà lái xe cần dừng lại cho khách nghỉ

Page 48: Chđề BD HS giỏi Tin 12

48

- Dòng thứ ba chứa chỉ số của k khách sạn mà xe dừng lại cho khách nghỉ (trong đó

nhất thiết phải bao gồm cả chỉ số của khách sạn thứ N)

Ví dụ:

TOURISM.INP TOURISM.OUT

4

300

250 310 550 590

500

2

2 4

Bài 5: Duyệt điểm

Xét lưới ô vuông tạo thành từ n*n dường, các đường của lưới được đánh số từ 1 đến n

từ trái qua phải và từ trên xuống dưới (1≤n≤20000). Ở mỗi hàng thứ i, người ta cho đoạn

thẳng xác định bởi hai điểm li và ri (1≤li≤ri≤n, i=1…n).

Yêu cầu: Xác định độ dài của đường đi ngắn nhất dọc theo các cạnh của lưới từ điểm

(1,1) đến điểm (n,n) và thoả mãn các điều kiện:

Chỉ đi sang phải, sang trái hoặc xuống dưới

Đi qua tất cả các điểm thuộc các đoạn thẳng đã cho

Dữ liệu: Vào từ file văn bản VISIT.INP:

Dòng đầu tiên chứa số nguyên dương n

Dòng thứ i trong n dòng sau chứa hai số nguyên dương li, ri

Kết quả: Đưa ra file văn bản VISIT.OUT một số nguyên duy nhất là độ dài của đường đi

ngắn nhất tìm được.

Ví dụ:

VISIT.INP VISIT.OUT

6

2 6

3 4

1 3

1 2

3 6

4 5

24

Bài 6: Những vị khách sộp vào nhà hàng

Một nhà hàng bắt đầu mở cửa tại thời điểm 0 và đóng cửa tại thời điểm T=2.109. Tại

cửa ra vào nhà hàng có treo một bảng hiện thị số. Tại thời điểm 0, số trên bảng là 0 và cứ

sau 1s số trên bảng giữ nguyên giá trị hoặc tăng, giảm một đơn vị. Bảng chỉ hiện thị được

các số không âm.

Page 49: Chđề BD HS giỏi Tin 12

49

Có N vị khách sộp đi qua nhà hàng, vị khách sộp thứ i đi qua nhà hàng tại thời điểm Ti

và sở thích của ông ta là số Si. Nếu như ở thời điểm ông ta đi qua nhà hàng biển số trước cửa

nhà hàng hiện đúng số ông ta thích thì ông ta sẽ vào và tiêu một số tiền là Pi.

Yêu cầu: Hãy giúp nhà hàng điều khiển bảng số sao cho tổng số tiền mà các vị khách sộp

vào nhà hàng là lớn nhất.

Input: Cho trong file WELCOME.INP

Dòng đầu tiên ghi số nguyên dương N (N200)

Trong N dòng tiếp theo, dòng thứ i ghi thông tin về vị khách thứ i gồm ba số nguyên

dương Ti, Si và Pi.

Output: Ghi ra file WELCOME.OUT một số nguyên duy nhất là tổng số tiền lớn nhất mà

nhà hàng nhận được.

Ví dụ:

WELCOME.INP WELCOME.OUT

3

2 1 3

3 2 4

1 3 10

7

Bài 7: Di chuyển từ Tây sang Đông

Cho hình chữ nhật M x N ô vuông (các hàng đánh số từ 1 đến M từ trên xuống và các

cột đánh số từ 1 đến N từ trái sang phải), mỗi ô vuông chứa một số nguyên. Có thể di

chuyển từ một ô sang một ô khác thuộc cột bên phải cùng dòng hoặc lệch một dòng. Tìm

cách di chuyển từ cột 1 sang cột N sao cho tổng các số của các ô đi qua là nhỏ nhất.

Input: Từ file văn bản WTOE.INP

Dòng đầu tiên chứa hai số nguyên M, N (1≤M,N≤100)

Tiếp theo là M dòng, mỗi dòng ghi N số nguyên là các số nằm trên các ô vuông của

hàng tương ứng bắt đầu từ cột 1 đến cột N. Các số nguyên này có giá trị tuyệt đối

không vượt quá 30000

Output: Ghi ra file văn bản WTOE.OUT

Dòng thứ nhất ghi tổng các số trong các ô đi qua

Dòng thứ 2 ghi N số lần lượt là chỉ số hàng của các ô trên hành trình bắt đầu từ cột 1

đến cột N

Ví dụ:

WTOE.INP WTOE.OUT

2 2

2 6

3 5

9

2 1

Page 50: Chđề BD HS giỏi Tin 12

50

Bài 8: Dãy con đối xứng dài nhất (daydx.pas)

Dãy số có A1, A2, …, AN được gọi là đối xứng nếu các cặp số ở các vị trí i và N-i+1

bằng nhau (với i=1..N). Cho trước một dãy số có N phần tử, mỗi phần tử là số nguyên. Hãy

tìm cách loại bỏ một số phần tử trong dãy để dãy thu được tạo thành một dãy đối xứng dài

nhất.

Dữ liệu vào: File văn bản DAYDX.INP có cấu trúc như sau:

+ Dòng 1: Số nguyên N (2≤N≤5000);

+ Dòng thứ 2 ghi N số nguyên là các số hạng trong dãy có giá trị tuyệt đối ≤1000, mỗi

số cách nhau một dấu cách.

Dữ liệu ra: File văn bản DAYDX.OUT với yêu cầu nhhư sau:

+ Dòng đầu ghi số nguyên M là số các các số hạng của dãy đối xứng tìm được;

+ Dòng thứ 2 ghi M số hạng của dãy tìm được, mỗi số cách nhau một dấu cách.

Ví dụ:

DAYDX.INP DAYDX.OUT

13

1 3 2 3 1 5 2 3 4 1 4 3

2

7

2 3 4 1 4 3 2

Bài 9: Nối mạng máy tính

Các học sinh khi đến thực tập trong phòng máy tính thường hay chơi trò chơi điện tử

trên mạng. Để ngăn ngừa, người trực phòng máy đã ngắt tất cả các máy tính ra khỏi mạng

và xếp chúng thành một dãy trên một cái bàn dài và gắn chặt máy xuống mặt bàn rồi đánh

số thứ tự các máy từ 1 đến N theo chiều từ trái sang phải. Các học sinh tinh nghịch không

chịu thua, họ đã quyết định tìm cách nối các máy trên bàn bởi các đoạn dây nối sao cho mỗi

máy được nối với ít nhất một máy khác. Để tiến hành công việc này, họ đã đo khoảng cách

giữa hai máy liên tiếp. Bạn hãy giúp các học sinh này tìm cách nối mạng thoả mãn yêu cầu

đặt ra sao cho tổng độ dài cáp nối phải sử dụng là ít nhất.

Dữ liệu: vào từ file văn bản CABLE.INP:

Dòng đầu tiên chứa số lượng máy N (1N25000)

Dòng thứ i trong số N-1 dòng tiếp theo chứa các khoảng cách từ máy i đến máy i+1

(i=1,2,...,N-1). Giả thiết rằng khoảng cách từ máy 1 đến máy N không vượt quá 106.

Kết quả: Ghi ra file văn bản CABLE.OUT độ dài của cáp nối cần sử dụng.

Ví dụ:

CABLE.INP CABLE.OUT

6

2

2

3

2

2

7

Page 51: Chđề BD HS giỏi Tin 12

51

Chuyên Đề:

LÝ THUYẾT ĐỒ THỊ

I. MỘT SỐ KHÁI NIỆM

Một đồ thị (Graph) G (V,E) bao gồm một tập hợp hữu hạn V các nút hay đỉnh (Vertices)

và một tập hữu hạn E các cặp đỉnh mà ta gọi là cung (edges)

Ví dụ: Trong thực tế ta bắt gặp nhiều đồ thị như mạng máy tính, mạng điện, mạng giao

thông….Bằng hình vẽ, đồ thị có thể mô tả như sau:

a) b)

Hình 1: Đồ thị

Nếu (v1,v2) là các cặp đỉnh thuộc E thì ta nói: có một cung nối v1 và v2 hay có đường đi

trực tiếp từ v1 đến v2. Nếu cung (v1, v2) khác cung (v2,v1) thì ta có một đồ thị định hướng

(hình 1.b), lúc đó (v1,v2) được gọi là cung định hướng. Nếu thứ tự các nút trên cung không

được coi trọng thì ta gọi là đồ thị vô hướng (hinh 1.a) tức là với mọi cung (v1,v2) trong đồ thị

G, nếu cung (v1,v2) bằng cung (v2,v1) thì đó là đồ thị vô hướng

Trong hình 1.a, đi từ đỉnh 1 đến đỉnh 5 ta có nhiều cách đi, Ví dụ: 1→2→5 hay

1→2→3→5. Ta gọi mỗi cách đi như vậy là một đường đi. Tổng quát: Một đường đi (path)

từ đỉnh vp đến đỉnh vq trong đồ thị G là một dãy đỉnh vp, vi1, vi2, vi3,…,vin , vq mà (vp,vi1),

(vi1, vi2),…,(vin,vq) là các cung của G.

Đường đi đơn (Simple Path) là đường đi mà mọi đỉnh trên đó, trừ đỉnh đầu và đỉnh cuối,

đều khác nhau

Một chu trình đơn (Cycle) là đường đi đơn mà trong đó đỉnh đầu và đỉnh cuối trùng nhau.

Ví dụ trong hình 1.a có một số chu trình: 1→2→4→1; 2→5→4→2.

Trong đồ thị G, hai đỉnh vi và vj gọi là liên thông (Connected) nếu tồn tại ít nhất một

đường đi từ vi đến vj

Một đồ thị G gọi là liên thông nếu với mọi cặp định phân biệt vi và vj trong V(G) đều có

đường đi từ vi đến vj. Hình 1.a là đồ thị liên thông còn đồ thị ở hình 1.b không liên thông vì

không có đường đi từ đỉnh 1 đến đỉnh 2

Trong đồ thị, đôi khi người ta cần gán một giá trị để thể hiện một thông tin liên quan đến

cung (gọi là trọng số), trong trường hợp này đồ thị được gọi là đồ thị có trọng số. Ví dụ,

Page 52: Chđề BD HS giỏi Tin 12

52

mạng lưới giao thông đường bộ giữa các thành phố với trọng số ứng với mỗi tuyến đường

giữa hai thành phố là độ dài của tuyến đường đó

Hình 2: Đồ thị có trọng số

+ Đối với đồ thị vô hướng G=(V,E). Xét một cạnh eE. nếu e=(u, v) thì ta nói hai đỉnh u và

v là kề nhau và cạnh e này liên thuộc với đỉnh u và đỉnh v

+ Với một đỉnh v trong đồ thì vô hướng, ta định nghĩa bậc (degree) của v, ký hiệu là deg(v)

là số cách cạnh liên thuộc với v

II. BIỂU DIỄN ĐỒ THỊ

1. Danh sách đỉnh kề

Đối với mỗi đỉnh trong đồ thị trong hình 3, ta có danh sách các đỉnh kề tương ứng:

Hình 3

Danh sách các đỉnh kề

Đỉnh Các đỉnh kề

A B, F

B A, C, , F

C B, D

D B, C, E, F

E D F

F A, B, D, E

Để lưu trữ đồ thị ở trong file, ta có thể sử dụng danh sách các đỉnh kể. Với đồ thị trong

hình 3, cấu trúc của file lưu trữ nó trong file DOTHI.INP như sau:

DOTHI.INP Giải thích

6

B F

A C D F

B D

B C E F

D F

A B D E

- Dòng đầu là số đỉnh của đồ thị

- Dòng thứ 2 là các đỉnh kề của đỉnh A

- Dòng thứ 3 là các đỉnh kề của đỉnh B

….

- Dòng thứ 7 là các đỉnh kề của đỉnh F

Page 53: Chđề BD HS giỏi Tin 12

53

2. Ma trận kề

Danh sách các đỉnh kề của đồ thị ở hình 3 có thể được biểu diễn lại bằng một ma trận A

có kích thước 6x6 trong đó giá trị của ô (i,j) thể đỉnh i và j kề nhau. Quy ước như sau:

Với quy ước đó, đồ thị ở hình 3 được biểu diễn lại bằng ma trận:

Đỉnh A B C D E F

A 0 1 0 0 0 1

B 1 0 1 1 0 1

C 0 1 0 1 0 0

D 0 1 1 0 1 1

E 0 0 0 1 0 1

F 1 1 0 1 1 0

Ví dụ : Ma trận kề của đồ thị có hướng G=(V,E) dưới đây sẽ được biểu diễn bởi ma trận:

Hình 4

A B C D E

A 0 1 1 0 1

B 0 0 0 1 0

C 0 1 0 1 0

D 1 0 0 0 0

E 0 0 0 1 0

+ Ma trận biểu diễn đồ thị vô hướng là ma trận đối xứng, điều này không đúng với đồ thị

có hướng

3. Danh sách cạnh

Ngoài hai cách biểu diễn trên ta có thể biểu diễn đồ thị bằng cách đưa ra tất cả các cạnh

của đồ thị

Đồ thị trong hình 3 được biểu diễn trong file DOTHI.INP như sau

DOTHI.INP Giải thích

A B

A F

B C

B D

B F

C D

D E

D E

E F

Mỗi dòng là một

cạnh của đồ thị

nối hai đỉnh kề

nhau

Page 54: Chđề BD HS giỏi Tin 12

54

III. DUYỆT ĐỒ THỊ

Cho đồ thị G=(V,E), duyệt đồ thị là cách thăm tất cả các đỉnh của đồ thị theo một trật tự

nào đó

Ví dụ: Cho đồ thị hình 5.a thì ta có hai trật tự thăm các đỉnh của đồ thị là tìm kiếm theo

chiều sâu (hình 5.b) và tìm kiếm theo chiều rộng (hình 5.c) với đỉnh xuất phát là đỉnh 1

a) b) c)

Hình 5

1. Tìm kiếm theo chiều sâu

a. Ý tưởng:

Xuất phát từ một đỉnh u trong đồ thị, ta lần lượt duyệt qua tất cả các lân cận v của u, nếu

v chưa được thăm thì thực hiện thăm v. Quá trình duyệt đồ thị được tiếp tục với đỉnh v sao

cho tất cả các đỉnh của đồ thị đều được thăm.

b. Giải thuật

PROCEDURE DFS(u);

{u là một đỉnh của đồ thị G}

BEGIN

Thăm u;

FOR các đỉnh kề v của u DO

IF v chưa được thăm THEN DFS(v)

END;

c. Chương trình:

Để viết được chương trình dựa vào giải thuật ở trên, ta cần trả lời các câu hỏi

+ Thăm u là thực hiện công việc gì? Đây là câu lệnh mang tính tổng quát, việc thăm u

còn tùy thuộc vào từng yêu cầu cụ thể để thực hiện các công việc khác nhau

+ Làm thế nào để xác định được v là đỉnh kề của u? Nếu ta biểu diễn đồ thị bằng ma trận

A thì chỉ cần kiểm tra giá trị A[u,v], nếu bằng 1 thì v là đỉnh kề của u

Page 55: Chđề BD HS giỏi Tin 12

55

+ Làm thế nào để biết được đỉnh v chưa được thăm? Khi thăm một đỉnh nào đó ta thực

hiện đánh dấu nó đã được thăm.

Thủ tục tìm kiếm đồ thị theo chiều sâu:

{A là ma trận biểu diễn đồ thị. Chon là mảng dùng để

đánh dấu khi 1 đỉnh được thăm}

PROCEDURE DFS(u:integer);

VAR v:integer;

BEGIN

Write(u:3); chon[u]:=true;

FOR v:=1 TO N DO

IF(Chon[v]=False)and(A[v,u]=1)THEN DFS(v);

END;

d. Mở rộng: {Khử đệ quy}

+ Nhắc lại về STACK: Đó là bộ nhớ do chương trình Pascal tự động tạo ra khi thực hiện

các chương trình con đệ quy, STACK có cơ chế hoạt động là “vào trước ra sau” tức là cái gì

được đưa vào STACK trước thì được lấy ra sau

+ Để chuyển được chương trình con đệ quy sang khử đệ quy ta chỉ cần tạo ra bộ nhớ

STACK có cơ chế đưa vào/lấy ra như trong chương trình đệ quy. Thông thường người ta

dùng kiểu mảng hoặc con trỏ để tạo bộ nhớ STACK.

+ Viết lại giải thuật theo cách khử đệ quy

+ Tham khảo thủ tục tìm kiếm theo chiều sâu bằng cách khử đệ quy

PROCEDURE DFS(u:integer);

VAR v,dinh:integer;

chon:Array[0..100]of boolean;

Stack:Array[0..100] of integer;

BEGIN

Fillchar(chon,sizeof(chon),false);

{Đưa 1 đỉnh vào Stack}

dinh:=1; Stack[1]:=u; chon[u]:=true;

write(u:3);

WHILE dinh>=1 DO

BEGIN

{lấy 1 đỉnh ra khỏi Stack}

u:=Stack[dinh];dinh:=dinh-1;

IF chon[u]=false THEN write(u:3);

chon[u]:=true;

Page 56: Chđề BD HS giỏi Tin 12

56

FOR v:=N DOWNTO 1 DO

IF(A[u,v]=1)and(chon[v]=false)THEN

BEGIN

{Đưa 1 đỉnh vào Stack}

dinh:=dinh+1; Stack[dinh]:=v;

END;

END;

END;

2. Tìm kiếm theo chiều rộng

a. Ý tưởng:

Xuất phát từ 1 đỉnh u trong đồ thị, sau khi thăm tất cả các đỉnh kề v của u, tiếp tục thăm

các đỉnh kề của v. Cứ tiếp tục như vậy cho đến khi tất cả các đỉnh của đồ thị được thăm.

b. Giải thuật:

Để thực hiện tìm kiếm theo chiều rộng ta sử dụng bộ nhớ Queue (ngược với Stack), cơ

chế của nó là “ vào trước ra trước, vào sau ra sau”

+ Giải thuật liệt kê từng bước

B1: Đưa u vào Queue

B2:Nếu Queue chưa rỗng thì lấy 1 đỉnh trong Queue ra và gán cho p. thăm p

B3: Xét tất cả các đỉnh kề v của p, nếu v chưa được thăm thì đưa vào Queue. Quay lại B2

+ Giải thuật giải mã Pascal

PROCEDURE BFS(u)

BEGIN

Push(u,Q); {đưa u vào Queue;}

WHILE Q chưa rỗng DO

BEGIN

u:=POP(Q); {lấy 1 đỉnh trong Queue ra và gán cho u}

Thăm u;

FOR các lân cận v của u DO

IF v chưa được thăm THEN Push(v,Q);

END;

END;

c. Chương trình:

Tham khảo thủ tục sau đây

PROCEDURE BFS(u:integer);

VAR v,dinh,dau:integer;

chon:Array[0..100]of boolean;

Q:Array[0..100] of integer;

Page 57: Chđề BD HS giỏi Tin 12

57

BEGIN

Fillchar(chon,sizeof(chon),false);

dinh:=1;

dau:=1;

write(u:3);

Q[1]:=u;

chon[u]:=true;

WHILE dinh>=dau DO

BEGIN

u:=Q[dau];

dau:=dau+1;

IF chon[u]=false THEN write(u:3);

chon[u]:=true;

FOR v:=N DOWNTO 1 DO

IF(A[u,v]=1)and(chon[v]=false)THEN

BEGIN

dinh:=dinh+1;Q[dinh]:=v;

END;

END;

END;

IV. TÍNH LIÊN THÔNG TRONG ĐỒ THỊ

Khái niệm liên thông trong đồ thị: Một đồ thị G gọi là liên thông nếu với mọi cặp định

phân biệt vi và vj trong V(G) đều có đường đi từ vi đến vj.

Bài toán: Cho một đồ thị G=(V,E) Hãy cho biết các đỉnh trong đồ thị liên thông với nhau

hay không?

Ta có thể áp dụng giải thuật phép duyệt đồ thị để kiểm tra tính liên thông trong đồ thị

bằng cách so sánh số lượng đỉnh được thăm với số đỉnh của đồ thị G. Nếu bằng nhau thì G

là liên thông, ngược lại không liên thông. Để biết được số đỉnh được thăm trong giải thuật

DFS hoặc BFS ta dùng thêm một biến để đếm. Hai giải thuật DFS và BFS có thể được sửa

lại như sau:

PROCEDURE DFS(u, dem);

{u là một đỉnh của đồ thị G}

BEGIN

Thăm u; inc(dem);

FOR các đỉnh kề v của u DO

IF v chưa được thăm THEN

DFS(v,dem)

END;

Page 58: Chđề BD HS giỏi Tin 12

58

PROCEDURE BFS(u, dem)

BEGIN

Push(u,Q); {đưa u vào Queue;}

WHILE Q chưa rỗng DO

BEGIN

u:=POP(Q); {lấy 1 đỉnh trong Queue ra và gán cho u}

Thăm u;inc(dem);

FOR các lân cận v của u DO

IF v chưa được thăm THEN Push(v,Q);

END;

END;

*Những thay đổi được gạch chân

Trong chương trình chính ta có thể viết như sau:

Dem≔0;

DFS(u); {BFS(u);}

IF dem=N THEN Write(‘Đồ thị liên thông’)

ELSE Write(‘Đồ thị không liên thông’);

Trong trường hợp đồ thị không liên thông thì đồ thị sẽ được chia thành nhiều tập đỉnh mà

trong mỗi tập đỉnh các đỉnh có đường đi với nhau nhưng không có đường đi đến các đỉnh

trong tập đỉnh khác. Mỗi tập đỉnh như vậy ta gọi là một thành phần liên thông trong đồ thị.

Ví dụ đồ thị sau có 3 thành phần liên thông:

Trong đó hai thành phần liên thông đầu tiên có 4 đỉnh, thành phần còn lại chỉ có một đỉnh

duy nhất. Ta dễ dàng thấy rằng trong thành phần liên thông đầu tiên có 4 đỉnh (1, 2, 3, 4) và

từ 1 đỉnh bất kỳ trong 4 đỉnh này có thể đi đến các đỉnh còn lại nhưng không có đường đi

đến các đỉnh 5, 6, 7, 8, 9. Đỉnh 9 cũng được xem là một thành phần liên thông vì nó không

có đường đi đến tất cả các đỉnh còn lại.

Hai giải thuật DFS và BFS chỉ thực hiện thăm được các đỉnh trong một thành phần liên

thông nên ta có thể sử dụng hai giải thuật này để liệt kê các thành phần liên thông trong đồ

thị.

Đoạn chương trình sau liệt kê tất cả các thành phần liên thông của đồ thị:

Page 59: Chđề BD HS giỏi Tin 12

59

FOR u=1 TO N DO

IF đỉnh u chưa được thăm THEN

BEGIN

DFS(u); {BFS(u)}

Writeln;

END;

Trong thủ tục DFS (BFS) khi thực hiện thăm 1 đỉnh thì in đỉnh đó ra màn hình

V. CHU TRÌNH TRONG ĐỒ THỊ

Khái niệm chu trình đơn: Một chu trình đơn (Cycle) là đường đi đơn mà trong đó đỉnh

đầu và đỉnh cuối trùng nhau.

Bài toán: Cho đồ thị G=(V,E). Hãy cho biết trong G có chu trình đơn hay không?

Dựa vào định nghĩa về chu trình đơn ta có thể giải quyết bài toán bằng cách trong quá

trình duyệt đồ thị, tại đỉnh đang xét nếu đỉnh đó có nhiều hơn 1 đỉnh kề đã được thăm thì kết

luận có chu trình đơn. Ngược lại nếu đã xét hết tất cả các đỉnh mà không có đỉnh nào như

trên thì ta kết luận trong G không có chu trình đơn.

Ví dụ 1: Cho đồ thị:

Trong đồ thị trên có một đường đi đơn là 1→2→3→4

Đỉnh đang

xét

Các đỉnh

trong đƣờng

đi đơn

Đỉnh kề đã có

trong đƣờng đi

đơn

Chu

trình

1 0 0 Không

2 1 1 Không

3 1, 2 2 Không

4 1, 2, 3 3, 1 Có

Như vậy trong đường đi đơn 1→2→3→4 khi xét đến đỉnh 4 ta thấy đỉnh 4 có hai đỉnh kề

(1 và 3) tuy nhiên 2 đỉnh này được có trong đường đi đơn nên ta kết luận đồ thị đã cho có

chu trình

Ví dụ 2: Cho đồ thị:

Page 60: Chđề BD HS giỏi Tin 12

60

Trong đồ thị trên có các đường đi đơn là (1→5→2); (1→5→3); (1→5→4). Trong các

đường đi đơn này chỉ có đỉnh 5 là có 2 đỉnh kề tuy nhiên khi xét đến đỉnh 5 thì luôn có 1

đỉnh kề đã có trong đường đi (đỉnh 1) đỉnh kề còn lại chưa xét. Để xét đến các đường đi đơn

trong dồ thị ta sử dụng thuật toán duyệt đồ thị nên ta cũng có thể áp dụng thuật toán này để

kiểm tra tính chu trình trong đồ thị.

Qua 2 ví dụ trên ta rút ra kết luận sau: Trong quá trình duyệt đồ thị, tại một đỉnh đang

được thăm, ta xét các đình kề của nó, nếu số lượng đỉnh kề của nó đã được thăm lớn hơn 1

thì đồ thị đang xét có chu trình.

Thuật toán sau đây áp dụng giải thuật DFS để kiểm tra tính chu trình trong đồ thị liên

thông, trong đó A là ma trận có kích thước N, u là đỉnh đang xét

FUNCTION KT(A,N,u):Boolean;

BEGIN

Kt≔false; dem≔0;

FOR v≔1 TO N DO

BEGIN

IF có cung (u,v) và v đã được thăm THEN inc(dem);

IF dem>1 THEN exit(true) {có chu trình}

ELSE IF có cung (u,v) và v chưa được thăm THEN KT(A,N,v);

END; exit(false); {không có chu trình}

END;

VI. ĐƢỜNG ĐI NGẮN NHẤT

1. Một nguồn một đích

a. Bài toán:

Cho đồ thị có trọng số G=(V,E), các đỉnh được đánh số từ 1 đến N. Yêu cầu tìm đường đi

ngắn nhất giữa hai đỉnh u và v cho trước

Ví dụ đồ thị G=(V,E) được cho như sau:

Một số đường đi từ đỉnh 1 đến đỉnh 2

+ 1→2 (độ dài 20)

+ 1→4→2 (độ dài 18)

+ 1→4→3→2 (độ dài 19)

+ 1→6→5→4→2 (độ dài 17)

+ 1→6→5→4→3→2 (độ dài 18)

Trong các đường đi liệt kê ở trên thì đường đi 1→6→5→4→2 có độ dài 17 là đường đi

ngắn nhất từ đỉnh 1 đến đỉnh 2. Như vậy để tìm đường đi ngắn nhất giữa hai điểm bất kỳ ta

có thể liệt kê tất cả các đường đi giữa hai đỉnh rồi so sánh để tìm ra đường đi ngắn nhất. Tuy

vậy, giữa hai đỉnh bất kỳ trong một đồ thị liên thông sẽ có bao nhiêu đường đi? Câu trả lời là

Page 61: Chđề BD HS giỏi Tin 12

61

vô số vì một cung trong đường đi có thể lặp lại nhiều lần. Để tìm đường đi ngắn nhất giữa

hai đỉnh bất kỳ ta dùng thuật toán Dijkstra. Trong ví dụ trên ta gọi đỉnh 1 là đỉnh nguồn,

đỉnh 2 là đỉnh đích

b. Giải thuật

Giải thuật Dijkstra tìm kiếm đường đi ngắn nhất từ đỉnh u đến đỉnh v trong đồ thị có N

đỉnh được lưu trữ bằng ma trận lân cận A, trong đó A[i,j] là giá trị đường đi của cung (i,j)

Thuật toán liệt kê từng bƣớc

Bước 0:

+ Khởi tạo tập S rỗng. Tập S dùng để đánh dấu một đỉnh đã được chọn hay chưa.

+ Tập D có N phần tử dùng để lưu trữ độ dài ngắn nhất từ đỉnh u đến các đỉnh còn

lại.Lúc đầu gán D[i]=A[i,u] với 1≤i≤N; m=u;

Bước 1: Nếu m=v thì kết thúc

Bước 1: Chọn đỉnh m sao cho khoảng cách từ m đến u là nhỏ nhất và m chưa có trong tập S

Bước 2: Đưa m vào tập S

Bước 3: Nếu i chưa đưa vào S và D[m]+A[m,i]<D[i] thì gán D[i]=D[m]+A[m,i]. Quay

lại B1

Thuật toán giải mã Pascal

PROCEDURE Dijkstra(A,u,v);

BEGIN

S=rỗng; D[i]:=A[u,i]; (1≤i≤N)

Đưa u vào S

m:=u;

WHILE m<>v DO

BEGIN

Chọn m sao cho mS và m=min(D[i]) (1≤i≤N)

Đưa m vào S

FOR tất cả lân cận i của m DO

IF iS và D[i]>D[m]+A[m,i] THEN D[i]:=D[m]+A[m,i]

END;

END;

Kết quả: D[v] là độ dài đường đi nhỏ nhất từ u đến v

Đối với đồ thị mô tả trong ví dụ trên,ta có thể mô tả quá trình tìm kiếm đường đi như sau:

Page 62: Chđề BD HS giỏi Tin 12

62

Bƣớc Tập S

Đỉnh

đƣợc

chọn

D 1 2 3 4 5 6

Khởi

tạo Rỗng 20 11 2

1 1 6 20 11 7 2

2 1, 6 5 20 10 7 2

3 1, 6, 5 4 17 16 10 7 2

4 1, 6, 5, 4 3 17 16 10 7 2

5 1, 6, 5, 4,

3 2

Ta có thể giải thích bảng trên như sau:

+ Khởi tạo: Lúc đầu tập S rỗng, giá trị của D[i]=A[i,u] (1≤i≤6), D[i]= nghĩa là không

có đường đi trực tiếp từ u đến i

+ Bước 1: Đưa đỉnh 1 vào S. Đỉnh m=6 được chọn vì 6S và D[6]=2 (nhỏ nhất trong

mảng D). Lúc này ta so sánh giá trị của D[i] và D[m]+A[m,i] để gán lại giá trị nhỏ hơn cho

D[i] với 1≤i≤6

Ta có:

i = 1 2 3 4 5 6

D[i]= 20 11 2

D[6]+A[6,i]= x 2+ 2+ 2+ 2+5 2+

* Lưu ý: Đỉnh 1 đã đưa vào tập S nên ta không thực hiện tính D[6]+A[6,1]

Trong bảng trên ta thấy D[6]+A[6,5] (=7)<D[5](= ) nên gán lại D[5]= D[6]+A[6,5],

các giá trị khác giữ nguyên

Sau khi gán lại, ta có giá trị của mảng D là

i= 1 2 3 4 5 6

D[i]= 20 11 7 2

+ Bước 2: Đưa đỉnh 6 đã chọn ở Bước 1 vào tập S. Đỉnh tiếp theo được chọn là đỉnh 5

(Do D[5]=7 là nhỏ nhất, đỉnh 6 và đỉnh 1 đã có trong S nên không được chọn). Thực hiện so

sánh và gán lại mảng D tương tự như Bước 1

+ ….

+ Bước 5: Đưa đỉnh 3 vào tập S. Đỉnh được chọn là đỉnh 2, do 2 trùng với đỉnh v (v=2)

nên thuật toán kết thúc.

Kết quả: D[v=2]=17 là giá trị nhỏ nhất của đường đi từ u (u=1) đến v (v=2);

Page 63: Chđề BD HS giỏi Tin 12

63

c. Nhận xét về giải thuật

+ So với thuật toán thông thường, thuật toán Dijkstra thực hiện nhanh hơn do không cần

phải tìm ra tất cả đường đi từ đỉnh nguồn đến đỉnh đích. Độ phức tạp của Dijkstra là O(n2)

do trong thuật toán sử dụng một vòng lặp WHILE – DO và 1 vòng lặp FOR – DO lồng nhau

+ Thuật toán trên chỉ mới đưa ra được giá trị nhỏ nhất của đường đi từ đỉnh nguồn đến

đỉnh đích chứ không chỉ ra đường đi giữa chúng

d. Vấn đề đường đi trong thuật toán Dijkstra

Ta có nhận xét như sau: Phép gán D[i]:=D[m]+A[m,i]chỉ được thực hiện khi điều kiện

D[i]>D[m]+A[m,i]được thỏa mãn, tức là giá trị của mảng D tại đỉnh i thay đổi, điều này

cũng đồng nghĩa với việc độ dài đường đi ngắn nhất từ đỉnh u đến đỉnh i sẽ bằng độ dài

đường đi ngắn nhất từ đỉnh u đến đỉnh m cộng với giá trị A[m,i], như vậy đường đi ngắn

nhất từ u đến i sẽ đi qua m. Kết luận m là đỉnh trước của i. Với kết luận này, trong thuật

toán Dijkstra ta cần phải lưu lại đỉnh trước của đỉnh i là m khi gán lại giá trị của D[i].

Lưu ý: Do lúc đầu ta khởi tại D[i]:=A[u,i] nên ta cũng khởi tạo đỉnh trước của đỉnh i là

đỉnh u.

Thuật toán được sửa lại như sau:

PROCEDURE Dijkstra(A,u,v);

BEGIN

S=rỗng;

D[i]:=A[u,i];luu[i]:=u; (1≤i≤N)

Đưa u vào S

m:=u;

WHILE m<>v DO

BEGIN

Chọn m sao cho mS và m=min(D[i]) (1≤i≤N)

Đưa m vào S

FOR tất cả lân cận i của m DO

IF iS và D[i]>D[m]+A[m,i] THEN

BEGIN

D[i]:=D[m]+A[m,i]

Luu[i]:=m;

END;

END;

END;

Kết quả: Đường đi ngắn nhất từ u đến v được lưu trong mảng một chiều LUU, giá trị của

đường đi ngắn nhất từ u đến v và D[v]

Đối với giải thuật trên ta có thể mô tả lại quá trình tìm kiếm đường đi như sau:

Page 64: Chđề BD HS giỏi Tin 12

64

Bƣớc Tập S

Đỉnh

đƣợc

chọn

D 1 2 3 4 5 6

Khởi

tạo 1

20

(1)

(1) 11

(1)

(1) 2

(1)

1 1, 6 6 20(1)

(5)

11(1)

7(6)

2(1)

2 1, 6, 5 5 20(1)

(5)

10(5)

7(6)

2(1)

3 1, 6, 5, 4 4 17(4)

16(4)

10(5)

7(6)

2(1)

4 1, 6, 5, 4, 3 3 17(4)

16(4)

10(5)

7(6)

2(1)

5 1, 6, 5, 4, 3,

2 2

* Giá trị của mảng LUU nằm ở chỉ số trên trong 5 cột cuối. LUU có giá trị

i 1 2 3 4 5 6

Luu[i] X 4 4 5 6 1

Với cách lưu đường đi như vậy ta có thể tìm đường đi ngắn nhất từ đỉnh 1 đến đỉnh 2

bằng cách dò ngược đường đi: đỉnh cuối cùng là đỉnh 2, trước 2 là 4, trước 4 là 5, trước 5 là

6, trước 6 là 1, suy ra đường đi ngắn nhất từ 1 đến 2 là: 2←4←5←6←1. Với mảng LUU

như trên, ta có đoạn chương trình in đường đi như sau:

WHILE u<>v DO

BEGIN

Write( u,‘→’);

U:=Luu[u];

END;

Write(v);

2. Một nguồn mọi đích

Bài toán: Cho đồ thị G=(V,E) có các đỉnh được đánh số thứ tự từ 1 đến N. Yêu cầu tìm

đường đi ngắn nhất từ đỉnh u đến tất cả các đỉnh còn lại.

Từ bài toán một nguồn một đích ta có thể áp dụng để tìm đường đi ngắn nhất từ một đỉnh

nguồn u đến tất cả các đỉnh còn lại bằng câu lệnh: FOR i:=1 TO n DO Dijkstr(A,u,i); Tuy

nhiên, trong ví dụ trên, thuật toán Dijkstra áp dụng cho việc tìm đường đi ngắn nhất từ đỉnh

1 đến đỉnh 2, tất cả các đỉnh đã được đưa vào tập S, điều này cũng đồng nghĩa với việc trong

mảng D, giá trị D[i] là độ dài đường đi ngắn nhất từ đỉnh 1 đến đỉnh i và trong mảng Luu

cũng tìm được đường đi ngắn nhất từ đỉnh 1 đến các đỉnh còn lại. Ví dụ:

+ Đỉnh 1 đến đỉnh 6: 6←1 (độ dài 2)

+ Đỉnh 1 đến đỉnh 5: 5←6←1 (độ dài 7)

+ Đỉnh 1 đến đỉnh 4: 4←5←6←1 (độ dài 10)

Page 65: Chđề BD HS giỏi Tin 12

65

+ Đỉnh 1 đến đỉnh 3: 3←4←5←6←1 (độ dài 16)

Để giải quyết một bài toán trên ta chỉ cần sửa lại thuật toán Dijkstra như sau:

PROCEDURE SHORTEST-PATH(A,u);

BEGIN

S=rỗng;

D[i]:=A[u,i];luu[i]:=u; (1≤i≤N)

Đưa u vào S

m:=u;

WHILE số đỉnh trong S<N DO

BEGIN

Chọn m sao cho mS và m=min(D[i]) (1≤i≤N)

Đưa m vào S

FOR tất cả lân cận i của m DO

IF iS và D[i]>D[m]+A[m,i] THEN

BEGIN

D[i]:=D[m]+A[m,i]

Luu[i]:=m;

END;

END;

END;

* Cách tìm đường đi dựa vào mảng LUU được thực hiện giống bài toán một nguồn một

đích

3. Bài toán mọi nguồn mọi đích

Trong thuật toán một nguồn mọi đích, kết quả là mảng LUU dùng để lưu đường đi ngắn

nhất từ 1 đỉnh đến các đỉnh còn lại. Bằng cách sử dụng mảng này ta có thể đưa ra đường đi

ngắn nhất cũng như độ dài ngắn nhất giữa hai đỉnh u và v bất kỳ bằng đoạn chương trình

sau:

DOdai:=0;

WHILE u<>v DO

BEGIN

Write(u,‘→’)

DOdai:=DOdai+A[u,luu[u]];

U:=luu[u];

END; Write(v);

Ngoài cách áp dụng giải thuật Dijkstra người ta thường dùng đến giải thuật Floyd để tìm

đường đi ngắn nhất giữa tất cả các đỉnh trong đồ thị

Page 66: Chđề BD HS giỏi Tin 12

66

VII. CÂY KHUNG VÀ CÂY KHUNG CỰC TIỂU

1. Cây khung

Bài toán: Trong một khu du lịch có N điểm vui chơi, người ta tính toán nếu phải xây hết

tất cả các con đường để đi từ một điểm bất kỳ đến các điểm còn lại thì tốn quá nhiều tiền, do

vậy chỉ cần xây dựng 1 số con đường cần thiết để từ 1 điểm có thể đi đến tất cả các điểm

còn lại. Ví dụ với khu du lịch có 6 điểm vui chơi, nếu xây dựng hết tất cả các con đường từ

1 đỉnh đến 5 đỉnh còn lại thì ta có mô hình 6.a, tuy nhiên ta chỉ cần xây dựng 5 con đường

như hình 6.b hoặc 6.c để tiết kiệm chi phí:

a) b) c)

Hình 6

Hình 6.b và 6.c là hai cách để xây dựng các con đường sao cho từ một đỉnh trong đồ thị

có đường đi đến tất cả các đỉnh còn lại. Ta có bài toán tổng quát: Cho đồ thị liên thông

G=(V,E), trong E đã có một số cạnh. Hãy tìm cách loại bỏ một số cạnh sao cho đồ thị vẫn

liên thông nhưng không có chu trình đơn. Ta nói đây là bài toán tìm cây khung của một đồ

thị. Đồ thị 6.b và 6.c là hai cây khung tìm kiếm theo chiều sâu và tìm kiếm theo chiều rộng

của đồ thị có đỉnh xuất phát là A. Ngoài ra ta có thể liệt kê một số cây khung khác của đồ thị

hình 6

Cây khung là đồ thị liên thông và không có chu trình đơn

2. Tìm cây khung theo chiều sâu và cây khung theo chiều rộng

Để tìm cây khung của một đồ thị ta ứng dụng hai giải thuật duyệt đồ thị là tìm kiếm theo

chiều sâu (DFS – Depth-First Search) và tìm kiếm theo chiều rộng (BFS-Breadth-First

Search)

Bài toán tổng quát: Cho đồ thị G=(V,E) gồm N đỉnh và M cung. Hãy tìm cây khung theo

chiều sâu và cây khung theo chiều rộng

Dữ liệu vào: Cho bởi tệp tin văn bản DFS.INP (Hình 5a)

Page 67: Chđề BD HS giỏi Tin 12

67

+ Dòng đầu ghi hai số nguyên N và M

+ M dòng tiếp theo, mỗi dòng gồm 2 số nguyên là một cung của đồ thị

Dữ liệu ra: được ghi vào 2 tập tin văn bản:

+ Tập tin SAU.OUT gồm N-1 dòng, mỗi dòng chứa 2 số nguyên là 1 cung trong cây

khung (hình 5b)

+ Tập tin RONG.OUT gồm N-1 dòng, mỗi dòng chứa 2 số nguyên là 1 cung trong cây

khung (hình 5c)

Ví dụ:

DFS.INP SAU.OUT RONG.OUT

8 10

1 2

1 3

2 4

2 5

3 6

3 7

4 8

5 8

6 8

7 8

1 2

2 4

4 8

8 5

8 6

6 3

3 7

1 2

1 3

2 4

2 5

3 6

3 7

4 8

a. Cây khung theo chiều sâu

Khi duyệt đồ thị theo DFS, ta sẽ có được một cây khung theo chiều sâu, tuy nhiên thủ tục

trên chỉ đưa ra thứ tự các đỉnh được duyệt. Trong bài toán ví dụ cần phải đưa ra các cung

của cây khung, do vậy khi đỉnh v thỏa mãn yêu cầu là đỉnh kề của u và v chưa được thăm thì

ta đưa ra cung (u,v) là một cung trong cây khung cần tìm. Thủ tục DFS được sửa lại:

PROCEDURE

DFS_KH(u:integer);

BEGIN

Đánh dấu u được chọn

FOR các lân cận v của u DO

IF v chưa được chọn THEN

BEGIN

In cung (u, v);

DFS(v);

END;

END;

Page 68: Chđề BD HS giỏi Tin 12

68

b. Cây khung theo chiều rộng

Tương tự như tìm cây khung theo chiều sâu, ta cũng chỉ cần biến đổi một chút trong giải

thuật BFS để tìm cây khung theo chiều rộng

PROCEDURE BFS(u)

BEGIN

Push(u,Q); {đưa u vào Queue;}

WHILE Q chưa rỗng đo

BEGIN

u:=POP(Q); {lấy 1 đỉnh trong Q ra và gán cho u}

FOR các lân cận v của u DO

IF v chưa được thăm THEN

BEGIN

In cung (u,v);

Push(v,Q);

END;

END;

END;

3. Cây khung cực tiểu

a. Khái niệm

Đối với bài toán tìm cây khung ta không quan tâm đến việc đó có phải là đồ thị G có

trọng số hay không vì chỉ cần tìm ra một đồ thị liên thông không có chu trình đơn có đầy đủ

các đỉnh của G là được. Tuy nhiên trong đồ thị có trọng số, các cây khung lại có giá trị khác

nhau, giá trị của cây khung là tổng trọng số của các cung trong cây khung đó, vấn đề là phải

tìm ra cây khung có giá trị nhỏ nhất. Cây khung như vậy gọi là cây khung cực tiểu

b. Bài toán:

Cho đồ thị có trọng số G= (V,E) gồm N đỉnh và M cung. Yêu cầu tìm ra cây khung có giá

trị nhỏ nhất của G

Ví dụ: Đồ thị hình 7 và cây khung cực tiểu tương ứng với nó

a. Đồ thị G b. Cây khung cực tiểu

Hình 7

Page 69: Chđề BD HS giỏi Tin 12

69

Cây khung cực tiểu cũng là một cây khung nên nó phải đảm bảo các tính chất là liên

thông và không có chu trình. Tất nhiên số cung của nó bằng n-1 đỉnh

Để giải bài toán này ta có nhiều cách khác nhau

Cách 1: Tìm tất cả các cây khung của đồ thị G rồi so sánh để có được cây khung có giá

trị nhỏ nhất.

Nhận xét: Đây là cách làm mà nhiều người sẽ nghĩ đến đầu tiên khi xây dựng cây khung

cực tiểu của một đồ thị, tuy nhiên cách làm này có nhiều nhược điểm như độ phức tạp lớn,

khó tìm cây khung, mất nhiều thời gian

Cách 2 (giải thuật Prim): Tạo một đồ thị T có 2 đỉnh thuộc cung có trọng số nhỏ nhất

trong G. Đưa cung (1, 3) có trọng số nhỏ nhất trong G vào T. Đối với hai đỉnh 1, 3 có các

cung liên thuộc là (1, 2); (1, 4); (2, 3); (3, 4); (3, 5); (3, 6) trong đó cung (3, 6) có trọng số nhỏ

nhất (bằng 10) nên ta đưa cung này vào T. 3 đỉnh 1, 3, 6 trong G có các cung liên thuộc với nó

là (1, 2); (1, 4); (2, 3); (3, 4); (3, 5); (4, 6), cung (4,6) có trọng số nhỏ nhất nên đưa (4,6) vào

T. Cung tiếp theo được đưa vào T là (1, 4) hoặc (2, 3), ta thấy rằng khi đưa (1, 4) vào T thì

trong T xuất hiện chu trình còn khi đưa (2, 3) vào thi không xuất hiện chu trình nên ta chọn (2,

3). Cung tiếp theo là (2, 5). Đến đây ta có T là đồ thị liên thông, không có chu trình, bao gồm

tất cả các đỉnh trong G và có 5 cung. Ta nói T là cây khung cực tiểu của G

Ý tưởng của cách làm trên là: lúc đầu chọn 1 cung nhỏ nhất trong G để đưa vào T, sau đó

chọn 1 cung nhỏ nhất trong G liên thuộc với 1 đỉnh trong T để đưa vào T với điều kiện cung

đưa vào T không tạo ra chu trình trong T. Quá trình này lặp lại đến khi số cung trong T bằng

số đỉnh -1

Cách 3 (Giải thuật Karuskal): Tạo đồ thị T bao gồm các đỉnh trong G và tập cung bằng

rỗng. Trong G, chọn cung có trọng số nhỏ nhất là (1, 3) để đưa vào T và loại khỏi G, tiếp

theo chọn cung (4, 6) để đưa vào T vì (4, 6) là nhỏ nhất trong G (sau khi xóa (1, 3)) và khi

đưa vào T không tạo ra chu trình. Với quy luật chọn cung nhỏ nhất trong G để đưa vào T và

không tạo ra chu trình trong T thì các cung tiếp theo được đưa vào T là (2, 5), (3, 6), (2, 3).

Chú ý: Sau khi chọn cung nhỏ nhất trong G, dù có đưa vào T hay không thì ta cũng thực

hiện xóa cung đó trong G.

Ý tưởng của cách làm trên là: Chọn cung nhỏ nhất trong G để đưa vào T với điều kiện nó

không tạo ra chu trình trong T, quá trình được lặp lại cho đến khi số cung trong T bằng số

đỉnh -1

Ý tưởng tìm cây khung cực tiểu theo cách 2 và 3 là ý tưởng của hai giải thuật Prim và

Karuskal. Bây giờ ta đi vào 2 giải thuật này

c. Giải thuật Prim

PROCEDURE Prim(G) {tìm cây khung cực tiểu

của đồ thị G theo giải thuật Prim}

BEGIN

Chọn cung nhỏ nhất trong G đưa vào T

FOR i:=1 TO số đỉnh-2 DO

BEGIN

Page 70: Chđề BD HS giỏi Tin 12

70

Đưa cung có trọng số nhỏ nhất trong G và

liên thuộc với 1 đỉnh trong T vào T

END; {T là cây khung cực tiểu của G}

d. Giải thuật Karuskal

PROCEDURE Karuskal(G) {tìm cây khung cực tiểu

của đồ thị G theo giải thuật Karuskal}

BEGIN

Tạo T rỗng;

WHILE số cung trong T< số đỉnh -1 DO

BEGIN

v:= cung trong G có trọng số nhỏ nhất;

Đưa v vào T;

IF T có chu trình THEN loại v khỏi T;

Loại v khỏi G;

END;

e. So sánh Prim và Karuskal

Trong Prim ta chọn cạnh có trọng số nhỏ nhất và liên thuộc với một đỉnh trong T, tức là v

sẽ có một đỉnh trong T và 1 đỉnh ngoài T, còn trong karuskal, ta cũng chọn 1 cạnh có trọng

số nhỏ nhất nhưng không nhất thiết phải liên thuộc với một đỉnh trong G. Chính vì vậy trong

Prim khi đưa v vào T ta không cần kiểm tra T có chu trình hay không.

BÀI TẬP

Bài 1: Dự tiệc bàn tròn

Có n nhà khoa học đánh số 1, 2, ..., n và 26 lĩnh vực khoa học ký hiệu A, B, C, ..., Z.

Thông tin về người thứ i được cho bởi một xâu ký tự Si gồm các chữ cái in hoa thể hiện

những lĩnh vực khoa học mà người đó biết.

Ví dụ: S2 = 'ABCXYZ' cho biết nhà khoa học thứ 2 có hiểu biết về các lĩnh vực A, B, C,

X, Y, Z.

Một lần cả n nhà khoa học đến dự một bữa tiệc. Chủ nhân của bữa tiệc định xếp n nhà

khoa học ngồi quanh một bàn tròn, nhưng một vấn đề khiến chủ nhân rất khó xử là các nhà

khoa học của chúng ta có hiểu biết xã hội tương đối kém, nên nếu như phải ngồi cạnh một ai

đó không hiểu biết gì về các lĩnh vực của mình thì rất khó nói chuyện.

Vậy hãy giúp chủ nhân xếp n nhà khoa học ngồi quanh bàn tròn sao cho hai người bất kỳ

ngồi cạnh nhau phải có ít nhất một lĩnh vực hiểu biết chung, để các nhà khoa học của chúng

ta không những ăn ngon mà còn có thể trò chuyện rôm rả.

Dữ liệu: Vào từ file văn bản PARTY.INP. Trong đó:

Dòng 1: Ghi số n

Page 71: Chđề BD HS giỏi Tin 12

71

n dòng tiếp theo, dòng thứ i ghi xâu ký tự Si

Kết quả: Ghi ra file văn bản PARTY.OUT gồm n dòng.

Dòng thứ i ghi nhà khoa học ngồi tại vị trí i của bàn (Các vị trí trên bàn tròn được

đánh số từ 1 đến n theo chiều kim đồng hồ)

Lƣu ý:

n 20

Nếu có nhiều cách xếp thì chỉ cần chỉ ra một cách

Nếu không có cách xếp thì ghi vào file PARTY.OUT một dòng: NO SOLUTION

Ví dụ:

PARTY.INP PARTY.OUT PARTY.INP PARTY.OUT PARTY.INP PARTY.OUT

6

AV

DIQR

DV

CQ

AC

DR

1

3

6

2

4

5

10

AX

BI

ABTX

AS

IK

KS

BE

AB

EK

AK

1

3

2

5

6

4

8

7

9

10

6

AB

BC

CD

DE

EF

FG

NO SOLUTION

Hƣớng dẫn:

Ta chuyển bài toán về đồ thị: Mỗi nhà khoa học tương ứng với một đỉnh của đồ thị, trên

đồ thị có cung (i,j) nếu nhà khoa học thứ i có chung 1 lĩnh vực với nhà khoa học thứ j.

Sau khi đã chuyển về đồ thị ta chỉ cần áp dụng thuật toán tìm kiếm chu trình đi qua tất cả

các đỉnh

Bài 2: Bữa tiệc

Một bữa tiệc lớn có N khách tham dự trong một căn nhà có nhiều phòng, mỗi người

khách đều được phát một phiếu ăn có ghi số thứ tự khi đến tham gia. Tuy nhiên họ có thể

không quen nhau. Để tránh việc xếp một người khách vào một phòng mà người đó không

biết tất cả mọi người trong phòng, ban tổ chức quyết định lập ra danh sách những người

quen biết nhau sau đó sắp xếp họ vào cùng một phòng. Danh sách nhanh chóng được lập ra

dưới dạng hai người quen nhau sẽ được ghi trên một dòng bằng số thứ tự của họ, tuy nhiên

công việc chia nhóm lại gặp khó khăn vì số lượng khách tham dự quá đông. Dựa vào danh

Page 72: Chđề BD HS giỏi Tin 12

72

sách đã có, hãy giúp ban tổ chức chia nhóm và cho biết số lượng khách của nhóm đông nhất.

Kích thước và số lượng của phòng không hạn chế

Dữ liệu vào: Tập tin văn bản NHOM.INP

+ Dòng đầu ghi số nguyên dương N (2≤N≤10000)

+ Các dòng tiếp theo ghi hai số x và y với ý nghĩa hai người khách có số thứ tự x và y

quen nhau

Dữ liệu ra: Ghi vào tập tin văn bản NHOM.OUT

+ Danh sách mỗi nhóm được ghi trên một dòng

+ Dòng cuối cùng là số lượng của nhóm khách đông nhất

Các số cách nhau ít nhất một dấu cách

Ví dụ:

NHOM.INP NHOM.OUT

10

1 6

1 7

2 3

2 8

3 8

3 9

4 5

4 10

5 10

8 9

1 6 7

2 3 8 9

4 5 10

4

Hƣớng dẫn: Áp dụng giải thuật tìm kiếm trên đồ thị để xác định các thành phần liên

thông

Bài 3: Cây khung

Cho đồ thị G=(V,E) có N đỉnh và M cung. Yêu cầu viết chương trình tìm cây khung theo

chiều sâu và cây khung theo chiều rộng của đồ thị G.

Dữ liệu vào: tập tin văn bản CAYKHUNG.INP

+ Dòng đầu gồm 2 số nguyên N và M

+ M dòng tiếp theo, mỗi dòng 2 số nguyên là một cung của G

Dữ liệu ra: tập tin văn bản CAYKHUNG.OUT

+ N-1 dòng đầu tiên, mỗi dòng ghi hai số nguyên là một cung trong cây khung theo chiều

sâu

Page 73: Chđề BD HS giỏi Tin 12

73

+ N-1 dòng tiếp theo, mỗi dòng ghi hai số nguyên là một cung trong cây khung theo

chiều rộng

Ví dụ:

CAYKHUNG.INP CAYKHUNG.OUT

6 8

1 2

1 5

2 3

2 6

3 6

3 4

4 6

5 6

1 2

2 3

3 4

4 6

6 5

1 2

1 5

2 3

2 6

3 4

Hƣớng dẫn: Sử dụng giải thuật tìm kiếm theo chiều rộng và tìm kiếm theo chiều sâu để

tìm cây khung tương ứng

Bài 4: Nối mạng

Cho N máy tính được đánh số từ 1 đến N. Chi phí nối mạng giữa máy i và máy j là Cij.

Yêu cầu: Tìm cách nối dây cáp sao cho các máy tính trong mạng là đều kết nối được với

nhau và chi phí là nhỏ nhất

Dữ liệu vào: Đọc từ file văn bản NOIMANG.INP có nội dung

+ Dòng đầu là số nguyên N

+ Tiếp theo là một ma trận kích thước NxN gồm các số nguyên, trong đó giá trị của ô (i,j)

cho biết khoảng cách từ máy i đến máy j. Trên cùng một dòng các số cách nhau một dấu

cách

Giới hạn: 2≤N≤1000

Dữ liệu ra: Ghi vào file văn bản NOIMANG.OUT gồm

+ Dòng 1 là chi phí nhỏ nhất

+ Từ dòng 2 trở đi mỗi dòng là một cặp cạnh được nối

NOIMANG.INP NOIMANG.OUT

6

0 16 3 12 0 0

16 0 12 0 7 0

37

1 3

2 5

Page 74: Chđề BD HS giỏi Tin 12

74

3 12 0 13 16 10

12 0 13 0 0 5

0 7 16 0 0 16

0 0 10 5 16 0

3 2

3 6

6 4

Hƣớng dẫn: Áp dụng giải thuật Karuskal hoặc Prim để tìm cây khung cực tiểu

Bài 5: Trong làng có một phú ông nổi tiếng khoe khoang, môt hôm thấy đám trẻ con đang

chơi, để khỏe ruộng đất của mình, phú ông thách đố: Phú ông có rất nhiều ruộng đất, các

thửa ruộng được đánh số từ 1 đến N và đều có đường đi đến nhau, đố trẻ con hãy chỉ ra tất

cả các cách để ông ta có thể đi từ thửa ruộng thứ i đến thửa ruộng thứ j sao cho mỗi thửa

ruộng được đi qua không quá 1 lần. Đám trẻ lần lượt kể ra các đường đi nhưng do số lượng

ruộng đất của phú ông quá nhiều nên kể mãi mà không hết.

Yêu cầu: Hãy lập trình để giúp trẻ con trong làng thực hiện liệt kê các đường đi theo yêu

cầu của phú ông

Dữ liệu vào: file văn bản DD.INP

+ Dòng đầu gồm 3 số nguyên N, u, v (2≤N≤1000; 1≤u,v≤N)

+ Các dòng tiếp theo gồm 2 số nguyên i và j là một cung trong đồ thị G, hai số cách nhau

bằng 1 dấu cách

Dữ liệu ra: file văn bản DD.OUT

+Mỗi đường đi được ghi trên một dòng

+ Hai số trên mỗi dòng cách nhau bằng 3 ký tự -->

Ví dụ:

DD.INP DD.OUT

5 1 5

1 2

1 3

1 4

2 4

2 5

3 4

4 5

1-->2-->4-->5

1-->2--> 5

1-->3--> 4-->2-->5

1-->3--> 4--> 5

1-->4-->2-->5

1-->4-->5

Bài 6: Bạn Nam vừa mới chuyển nhà đến thành phố XYZ, ở thành phố này Nam có một

người bạn thân là An, sau khi liên lạc An và Nam quyết đinh gặp nhau tại một quán café

trong thành phố. Vì mới chuyển đến nên Nam không biết chọn địa điểm nào và cũng không

biết đường đi. Địa chỉ nhà của An, Nam và các quán café trong thành phố được đánh số từ 1

đến N, hãy giúp 2 bạn tìm quán café gần nhà hai bạn nhất và chỉ đường cho Nam đến quán.

Page 75: Chđề BD HS giỏi Tin 12

75

Dữ liệu vào: từ file DIADIEM.INP

+ Dòng đầu ghi 3 số nguyên N, u, v trong đó u và v là địa điểm của nhà bạn Nam và An

+ Các dòng tiếp theo mỗi dòng ghi 3 số nguyên x, y, z trong đó z là độ dài đường đi từ

điểm x đến y

Dữ liệu ra: ghi vào file DIADIEM.OUT

+ Dòng đầu là địa điểm m của quán café mà hai bạn chọn

+ Dòng thứ 2 là tổng tổng độ dài đường đi của hai bạn Nam và An đến m

+ Dòng thứ 3 là đường đi từ nhà bạn Nam đến quán café m

Ví dụ:

DIADIEM.IPN DIADIEM.OUT

7 3 5

1 2 14

1 7 10

2 3 13

1 5 15

3 4 28

3 5 30

3 7 35

4 5 40

5 6 3

6 7 5

36

6

6←5←3

Hƣớng dẫn: Áp dụng giải thuật Dijkstral để tìm đường đi ngắn nhất từ nhà An và nhà

Nam đến tất cả các quan café trong thành phố, quán café được chọn là quán có tổng độ dài

ngắn nhất từ nhà Nam và An đến quán đó

Page 76: Chđề BD HS giỏi Tin 12

76

MỤC LỤC Chuyên đề: PHÂN TÍCH THIẾT KẾ THUẬT TOÁN................................................................... 1 PHẦN 1: THUẬT TOÁN VÀ PHÂN TÍCH THUẬT TOÁN ........................................................ 1

I. KHÁI NIỆM BÀI TOÁN VÀ THUẬT TOÁN ......................................................................... 1 1. Khái niệm bài toán ................................................................................................................... 1

2. Khái niệm thuật toán: ............................................................................................................... 1 3. Các bước giải bài toán trên máy tính:....................................................................................... 1

a. Xác định bài toán: ............................................................................................................ 1 b. Thiết kế hoặc lựa chọn thuật toán: ................................................................................... 2 c. Viết chương trình: ............................................................................................................. 2

d. Kiểm thử và hiệu chỉnh: ................................................................................................... 2

e. Viết tài liệu ........................................................................................................................ 3

II. PHÂN TÍCH THUẬT TOÁN................................................................................................... 3 1. Độ phức tạp của thuật toán: ...................................................................................................... 3

a. Dùng mẫu chuẩn .............................................................................................................. 3 b. Phân tích thuật toán ......................................................................................................... 3

2. Kí pháp độ phức tạp của thuật toán .......................................................................................... 4 3. Các cách xác định độ phức tạp của thuật toán.......................................................................... 4

a. Quy tắc hằng số ................................................................................................................ 4 b. Quy tắc cộng ..................................................................................................................... 4 c. Quy tắc lấy max ................................................................................................................ 4

d. Quy tắc nhân .................................................................................................................... 5

4. Các thuật ngữ thường dùng cho độ phức tạp thuật toán ........................................................... 5

5. Đánh giá thời gian thực hiện chương trình .............................................................................. 5 6. Một số ví dụ ............................................................................................................................. 6

BÀI TẬP .......................................................................................................................................... 7

PHẦN 2: THIẾT KẾ THUẬT TOÁN .............................................................................................. 9 I. THUẬT TOÁN DUYỆT ............................................................................................................. 9

1. Phương pháp: ............................................................................................................................ 9 2. Giải các bài toán cấu hình tổ hợp bằng thuật toán duyệt ........................................................ 10

a. Tổ hợp: ........................................................................................................................... 10 b. Chỉnh hợp lặp: ................................................................................................................ 12

c. Chỉnh hợp không lặp: ..................................................................................................... 13 3. Bài toán 8 quân hậu ................................................................................................................ 15

BÀI TẬP ........................................................................................................................................ 17 II. THUẬT TOÁN SẮP XẾP ....................................................................................................... 18

1. Tầm quan trọng của bài toán sắp xếp: .................................................................................... 18

2. Sắp xếp trong và sắp xếp ngoài .............................................................................................. 18 3. Phát biểu bài toán: .................................................................................................................. 18 4. Các thuật toán sắp xếp đơn giản: ............................................................................................ 19

a. Sắp xếp nổi bọt (sắp xếp tráo đổi - Bubble Sort): .......................................................... 19 b. Sắp xếp chọn (Selection Sort): ....................................................................................... 20

c. Sắp xếp thêm dần (sắp xếp xen - Insertion Sort): ........................................................... 22

5. Thuật toán sắp xếp nhanh (QuickSort): .................................................................................. 23 6. Thuật toán sắp xếp hòa nhập (trộn) hai đường trực tiếp (MergeSort): ................................... 24

a. Trộn hai dãy đã sắp xếp: ................................................................................................ 24 b. Hòa nhập hai đường trược tiếp ...................................................................................... 26

BÀI TẬP ........................................................................................................................................ 27

Page 77: Chđề BD HS giỏi Tin 12

77

Chuyên đề: PHƢƠNG PHÁP QUY HOẠCH ĐỘNG ................................................................... 31 I. GIỚI THIỆU ............................................................................................................................. 31 II. CẤU TRÚC CHUNG CỦA CHƢƠNG TRÌNH CHÍNH: ................................................... 32 III. CÁC BƢỚC ĐỂ GIẢI BÀI TOÁN QUY HOẠCH ĐỘNG: .............................................. 32

IV. MỘT SỐ VÍ DỤ ..................................................................................................................... 32 1. Dãy Fibonaci: ......................................................................................................................... 32 2. Tổ hợp chập k của n phần tử: ................................................................................................. 33 3. Dãy con không giảm liên tục dài nhất .................................................................................... 34 4. Tìm dãy con không giảm dài nhất: ......................................................................................... 34

5. Bài toán balô 1: ....................................................................................................................... 36 6. Bài toán ba lô 2 ....................................................................................................................... 38 7. Bài toán ba lô 3 ....................................................................................................................... 40

8. Bài toán xâu con chung dài nhất ............................................................................................ 42 9. Bài toán chia kẹo .................................................................................................................... 44 10. Bài toán đổi tiền ................................................................................................................... 44

V. BÀI TẬP ................................................................................................................................... 45

Chuyên Đề: LÝ THUYẾT ĐỒ THỊ ................................................................................................ 51 I. MỘT SỐ KHÁI NIỆM ............................................................................................................. 51

II. BIỂU DIỄN ĐỒ THỊ ............................................................................................................... 52

1. Danh sách đỉnh kề ................................................................................................................ 52 2. Ma trận kề ............................................................................................................................. 53 3. Danh sách cạnh ..................................................................................................................... 53

III. DUYỆT ĐỒ THỊ .................................................................................................................... 54 1. Tìm kiếm theo chiều sâu ........................................................................................................ 54

a. Ý tưởng: .......................................................................................................................... 54 b. Giải thuật ........................................................................................................................ 54 c. Chương trình: ................................................................................................................. 54

d. Mở rộng: {Khử đệ quy} .................................................................................................. 55 2. Tìm kiếm theo chiều rộng ...................................................................................................... 56

a. Ý tưởng: .......................................................................................................................... 56 b. Giải thuật: ...................................................................................................................... 56 c. Chương trình: ................................................................................................................. 56

IV. TÍNH LIÊN THÔNG TRONG ĐỒ THỊ .............................................................................. 57

V. CHU TRÌNH TRONG ĐỒ THỊ ............................................................................................. 59 VI. ĐƢỜNG ĐI NGẮN NHẤT .................................................................................................... 60

1. Một nguồn một đích ............................................................................................................... 60 a. Bài toán: ........................................................................................................................ 60 b. Giải thuật ........................................................................................................................ 61 c. Nhận xét về giải thuật ..................................................................................................... 63 d. Vấn đề đường đi trong thuật toán Dijkstra .................................................................... 63

2. Một nguồn mọi đích ............................................................................................................... 64 3. Bài toán mọi nguồn mọi đích ................................................................................................. 65

VII. CÂY KHUNG VÀ CÂY KHUNG CỰC TIỂU .................................................................. 66 1. Cây khung ............................................................................................................................... 66

2. Tìm cây khung theo chiều sâu và cây khung theo chiều rộng ................................................ 66 a. Cây khung theo chiều sâu ............................................................................................... 67 b. Cây khung theo chiều rộng ............................................................................................. 68

3. Cây khung cực tiểu ................................................................................................................. 68 a. Khái niệm ....................................................................................................................... 68

Page 78: Chđề BD HS giỏi Tin 12

78

b. Bài toán: ......................................................................................................................... 68

c. Giải thuật Prim ............................................................................................................... 69 d. Giải thuật Karuskal ........................................................................................................ 70 e. So sánh Prim và Karuskal .............................................................................................. 70

BÀI TẬP ........................................................................................................................................ 70

TÀI LIỆU THAM KHẢO

[1]. Tài liệu giáo khoa chuyên Tin Quyển 1, 2, 3 – Hồ Sỹ Đàm (chủ biên)

[2]. Tuyển chọn các bài toán Tin học – Nguyễn Xuân My (chủ biên)

[3]. 150 Bài toán Tin học – Lê Minh Hoàng

[4]. Một số vấn đề chọn lọc trong Tin Học – Nguyễn Xuân My

[5]. Cấu trúc dữ liệu và giải thuật – Lê Minh Hoàng

[6]. Cấu trúc dữ liệu và giải thuật – Đỗ Xuân Lôi

[7]. Giải thuật và lập trình – Lê Minh Hoàng

[8]. Programming Challenges – Miguel A. Revilla

[9]. The Algorithm Design Manual – Steven S. Skiena