Tải bản đầy đủ (.doc) (24 trang)

Ứng dụng phương pháp Nhánh cận trong dạy và học chuyên tin tại trường THPT Chuyên Quảng Bình (TT)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (172.67 KB, 24 trang )

MỞ ĐẦU
1. Tính cấp thiết của đề tài
Chương trình đào tạo và bồi dưỡng học sinh có năng khiếu
Toán, Tin học bậc Trung học phổ thông đã được thực hiện trong
nhiều năm qua. Qua những năm thực hiện nó như là một chu trình
đặc biệt gắn với sự trưởng thành và hoàn thiện một mô hình đào tạo
đặc biệt. Đó là đào tạo mũi nhọn, đào tạo các thế hệ học sinh có năng
khiếu trong lĩnh vực Toán học, Tin học. Lớp lớp các thế hệ thầy và
trò đã dũng cảm đi lên, tìm tòi và sáng tạo để tiếp cận với thế giới
hiện đại, cập nhật thông tin và nghiên cứu các phương pháp. Gắn với
việc đổi mới phương pháp dạy và học của chương trình đào tạo
chuyên, ngành Giáo dục và Đào tạo đang tích cực đổi mới phương
pháp dạy và học để đào tạo những thế hệ học sinh giỏi có kết quả cao
trong các kỳ thi học sinh giỏi cấp Quốc gia và giành được nhiều huy
chương trong các kỳ thi Olympic quốc tế mà trong đó có kỳ thi
Olympic Tin học.
Có thể nói, giáo dục mũi nhọn phổ thông đã thu được những
thành tựu rực rỡ, được Nhà nước đầu tư có hiệu quả, xã hội thừa
nhận và bạn bè quốc tế khâm phục. Các đội tuyển quốc gia tham dự
các kỳ thi olympic quốc tế có bề dày thành tích mang tính ổn định và
có tính kế thừa. Đặc biệt, đội tuyển Tin học quốc gia tham dự thi
Olympic quốc tế đã đạt được nhiều thành tích nỗi bật. Tuy ra đời
muộn hơn hệ chuyên Toán nhưng hệ THPT chuyên Tin học đã sớm
khẳng định vị thế của mình, đang trên đà hoàn thiện và phát triển
đúng hướng.
Để nâng cao chất lượng đào tạo mũi nhọn khối chuyên Tin học
trường THPT Chuyên Quảng Bình các thầy cô giáo luôn luôn cố
1
gắng đổi mới phương pháp, xây dựng các chuyên đề dạy và học các
thuật toán trong tin học để nâng cao chất lượng cũng như hiệu quả
giáo dục. Để giải quyết tốt bài toán thì học sinh cần phải có một số


kỹ thuật quan trọng trong việc tiếp cận bài toán và tìm thuật toán. Có
rất nhiều lớp thuật toán khác nhau như: Vét cạn; Chia để trị; Quy
hoạch động; Tham lam; Nhánh cận; Các thuật toán trên đồ thị; Các
thuật toán trên Cây… Trong các lớp thuật toán trên tôi chọn đề tài
“Ứng dụng phương pháp Nhánh cận trong dạy và học chuyên tin tại
trường THPT Chuyên Quảng Bình”.
Các bài toán trên thực tế có muôn hình muôn vẻ, không thể
đưa ra một cách thức chung để tìm giải thuật cho mọi bài toán.
Chúng ta chỉ khảo sát một vài bài toán cụ thể và học cách nghĩ, cách
tiếp cận vấn đề, cách thiết kế giải thật. Từ đó rèn luyện kỹ năng linh
hoạt khi giải các bài toán thực tế.
2. Mục tiêu nghiên cứu
Với mục tiêu giáo dục là đào tạo nhân tài cho quê hương và
đất nước cho nên việc đổi mới phương pháp dạy và học của giáo viên
và học sinh khối chuyên tin đang ngày càng được đẩy mạnh. Đề tài
“Ứng dụng phương pháp Nhánh cận trong dạy và học chuyên tin tại
trường THPT Chuyên Quảng Bình” được thực hiện nhằm nâng cao
chất lượng và hiệu quả đào tạo. Tạo điều kiện tốt nhất cho học sinh
giành được các giải cao trong các kỳ thi học sinh giỏi môn tin học
cấp tỉnh, cấp quốc gia và hướng đến có giải trong kỳ thi olympic tin
học quốc tế.
Đổi mới phương pháp dạy và học của khối chuyên tin tại
trường THPT Chuyên Quảng Bình. Từ đó, giáo viên và học sinh nắm
được phương pháp nhánh cận thông qua việc khảo sát một số bài
toán cụ thể, tiêu biểu. Học được cách nghĩ, cách tiếp cận vấn đề,
2
cách thiết kế giải thuật và rèn luyện kỹ năng linh hoạt khi giải các bài
toán. Vận dụng được thuật toán nhánh cận vào giải một số bài toán
cụ thể để giáo viên và học sinh tiếp cận với phương pháp nhánh cận
một cách nhanh chóng và hiệu quả. Nắm bắt được một số kỹ thuật

quan trọng trong việc tiếp cận bài toán, tìm và thiết kế thuật toán.
3. Đối tượng và phạm vi nghiên cứu
Tìm hiểu phương pháp nhánh cận, mục tiêu và kế hoạch dạy
học chương trình chuyên sâu môn tin học để thực hiện việc đổi mới
phương pháp trong việc dạy học thuật toán tại trường THPT Chuyên
Quảng Bình. Trên cơ sở đó tiến phân tích và thiết kế thuật toán và cài
đặt các bài toán tiểu biểu có thể giải được bằng phương pháp nhánh
cận. Các bài toán được cài đặt chương trình bằng ngôn ngữ lập trình
Free Pascal để minh họa quá trình thực hiện thuật toán.
4. Phương pháp nghiên cứu
Bài toán đặt ra trong thực tế yêu cầu tìm ra một nghiệm thỏa
mãn một số điều kiện nào đó và nghiệm đó là tốt nhất theo một chỉ
tiêu cụ thể, đó là lớp bài toán tối ưu. Nghiên cứu lời giải các lớp bài
toán tối ưu thuộc về lĩnh vực quy hoạch toán học.
Tuy nhiên cũng cần phải nói rằng trong nhiều trường hợp
chúng ta chưa thể xây dựng một thuật toán nào thực sự hữu hiệu để
giải bài toán tối ưu, mà cho tới nay việc tìm nghiệm của chúng vẫn
phải dựa trên mô hình liệt kê toàn bộ các cấu hình có thể và đánh giá,
tìm ra cấu hình tốt nhất. Việc tìm cấu hình theo cách này còn có tên
gọi là vét cạn. Chính nhờ kỹ thuật này cùng với sự phát triển của
máy tính điện tử mà nhiều bài toán khó đã tìm thấy lời giải.
Mô hình thuật toán quay lui là tìm kiếm trên một cây phân
cấp. Nếu giả thiết rằng mỗi nút nhánh của cây chỉ có 2 nút con thì
cây có độ cao n sẽ có tới 2
n
nút lá, con số này lớn hơn rất nhiều lần
3
so với kích thước dữ liệu đầu vào n. Chính vì vậy mà nếu như ta có
thao tác thừa trong việc chọn x
i

thì sẽ phải trả giá rất lớn về chi phí
thực thi thuật toán bởi quá trình tìm kiếm lòng vòng vô nghĩa trong
các bước chọn kế tiếp x
i + 1
, x
i + 2
, … Khi đó, một vấn đề đặt ra là
trong quá trình liệt kê lời giải ta cần tận dụng những thông tin đã tìm
được để loại bỏ sớm những phương án chắc chắn không phải tối ưu.
Kỹ thuật đó gọi là kỹ thuật đánh giá nhánh cận trong tiến trình quay
lui.
- Thu thập tài liệu: Tìm các cuốn sách, các tài liệu viết về nội
dung giải toán bằng phương pháp nhánh cận. Các tài liệu viết về các
ngôn ngữ lập trình. Các bài toán tiêu biểu có thể giải được bằng
phương pháp nhánh cận. Các phần mềm ứng dụng mô phỏng thuật
toán.
- Nghiên cứu lý thuyết: Trên cơ sở các tài liệu thu thập được,
tiến hành đọc, phân loại và viết báo cáo.
- Cài đặt chương trình: Cài đặt chương trình cho các thuật toán
đã được xây dựng, thực hiện và kiểm tra chương trình. Cài đặt ứng
dụng mô phỏng cho các thuật toán.
5. Bố cục của đề tài
Chương 1: Phương pháp nhánh cận
Nội dung của chương là trình bày thuật toán đệ quy. Phát biểu
bài toán tối ưu tổ hợp và thuật toán giải bài toán tối ưu tổ hợp bằng
phương pháp nhánh cận.
Chương 2: Chương trình chuyên sâu môn tin học trường
THPT chuyên.
Nội dung của chương là trình bày phân phối chương trình môn
Tin học dành cho học sinh chuyên tin ở trường THPT chuyên.

4
Chương 3: Sử dụng phương pháp nhánh cận giải một số bài
toán tiêu biểu.
Nội dung của chương là phát biểu các bài toán, phân tích và
thiết kế thuật toán, cài đặt chương trình cho các bài toán.
6. Tổng quan tài liệu tham khảo
Khi triển khai nghiên cứu đề tài tôi đã tham khảo các tài liệu
về toán rời rạc, lý thuyết đồ thị, cấu trúc dữ liệu và giải thuật, toán
ứng dụng, các giải thuật nâng cao và ngôn ngữ lập trình Turbo
Pascal.
5
CHƯƠNG 1
PHƯƠNG PHÁP NHÁNH CẬN
1.1. KỸ THUẬT ĐỆ QUY
1.1.1. Thuật toán quay lui
Thuật toán quay lui dùng để giải bài toán liệt kê các cấu hình.
Thuật toán này làm việc theo cách:
- Mỗi cấu hình được xây dựng bằng cách xây dựng từng phần tử.
- Mỗi phần tử được chọn bằng cách thử tất cả các khả năng.
Giả sử cấu hình hình cần liệt kê có dạng x
1
, x
2
, … x
n
, khi đó
thuật toán quay lui sẽ xét tất cả các giá trị x
1
có thể nhận, thử cho x
1

nhận lần lượt các giá trị đó. Với mỗi giá trị thử gán cho x
1
, thuật toán
sẽ xét tất cả các giá trị x
2
có thể nhận, lại thử cho x
2
nhận lần lượt các
giá trị đó. Với mỗi giá trị thử gán cho x
2
lại xét tiếp các khả năng
chọn x
3
, cứ tiếp tục như vậy… Mỗi khi ta tìm được đầy đủ một cấu
hình thì liệt kê ngay cấu hình đó.
Từ những phân tích trên ta xây dựng mô hình quay lui như sau:
//Thủ tục này thử cho x[i] nhận lần lượt các giá trị mà nó có
thể nhận
Procedure Try(i);
Begin
For «Mọi giá trị v có thể gán cho x[i]» do
If «Gán được» then
Begin
«Cho x[i] := v»;
«Ghi nhận việc cho x[i] nhận giá trị V (nếu cần)»;
if «x[i] là phần tử cuối cùng trong cấu hình» then
«Thông báo cấu hình tìm được»
else
6
Try(i + 1); //Gọi đệ quy để chọn tiếp x[i+1]

«Nếu cần, bỏ ghi nhận việc thử x[i] := v để thử giá
trị khác»;
end;
End;
1.1.2. Giải bài toán bằng cách sử dụng Đệ quy quay lui
1.2. PHƯƠNG PHÁP NHÁNH CẬN
1.2.1. Bài toán tối ưu tổ hợp
Trong rất nhiều vấn đề ứng dụng thực tế của tổ hợp, các cấu
hình của tổ hợp còn được gán cho một giá trị bằng số đánh giá giá trị
sử dụng của cấu hình đối với mục đích sử dụng cụ thể nào đó.
Khi đó xuất hiện bài toán: Hãy lựa chọn trong số các cấu hình
tổ hợp chấp nhận được cấu hình có giá trị sử dụng tốt nhất. Các bài
toán như vậy chúng ta sẽ gọi là bài toán tối ưu tổ hợp.
Dạng tổng quát bài toán tối ưu tổ hợp phát biểu như sau:
Tìm cực tiểu (hay cực đại) của hàm
( )
min(max),f x
→
với
điều kiện
x D

, trong đó
D
là tập hữu hạn các phần tử.
Hàm
( )
f x
được gọi là hàm mục tiêu của bài toán, mỗi phần tử
x D


được gọi là một phương án còn tập
D
gọi là tập các phương án
của bài toán.
Tập
D
được mô tả như là tập các cấu hình tổ hợp thỏa mãn
một số tính chất cho trước nào đó.
Phương án
*
x D

đem lại giá trị nhỏ nhất (lớn nhất) cho hàm
mục tiêu được gọi là phương án tối ưu, khi đó
( )
* *
f f x=
được gọi
là giá trị tối ưu của bài toán.
1.2.2 Cách giải một bài toán bằng phương pháp nhánh cận
Ta sẽ mô tả tư tưởng của thuật toán trên mô hình bài toán tối
ưu tổ hợp tổng quát sau:
7
( )
{
}
min :
n
f x x D R

∈ ⊂
,
trong đó D là tập hữu hạn phần tử, là miền các phương án; x là
một phương án thuộc miền D.
Phương pháp nhánh cận được mô tả như sau:
Từ miền D ta phân nhánh thành hai miền D
1
, D
2
, trên mỗi
nhánh ta xây dựng hàm g xác định trên các miền D
1
, D
2
để tính giá trị
cận dưới.
Gọi
1
β
là cận dưới của nhánh D
1
, gọi
2
β
là cận dưới của
nhánh D
2
.
So sánh giá trị các cận dưới
1

β

2
β
, Nếu
1
β

2
β
thì bước
tiếp theo sẽ chọn nhánh D
1
để tiếp tục, ngược lại thì chọn nhánh D
2
.
Giả sử tại đây ta chọn nhánh D
1
tiếp tục phát triển thuật toán,
thì lúc này ta cũng phân nhánh D
1
thành hai nhánh D
11
và D
12
. Tiếp
tục dùng hàm g để tính cận dưới cho các nhánh D
11
và D
12

. Tương tự
ta gọi Gọi
11
β
là cận dưới của nhánh D
11
, gọi
12
β
là cận dưới của
nhánh D
12
. Lại tiếp tục so sách các giá trị cận dưới
11
β

12
β
để
chọn nhánh cần phát triển trong bước tiếp theo. Quá trình trên cứ lặp
đi lặp lại cho đến khi không thể phân nhánh được nữa, lúc này ta có
phương án tối ưu tạm thời trong quá trình tìm nghiệm của bài toán.
Gọi
*
β
là nghiệm tối ưu tạm thời, lúc này thuật toán nhánh
cận sẽ tiếp tục với những nhánh còn lại sao cho giá trị của hàm g tại
đó nhỏ hơn
*
β

.
Ta xây dựng thuật toán nhánh cận bằng thủ tục đệ quy tổng
quát như sau:
Procdure Try(k)
(* Phát triển phương án bộ phận (a
1
, a
2
, …a
k-1
) theo thuật toán
quay lui có kiểm tra cận dưới trước khi tiếp tục phát triển phương
án*)
8
Begin
For a
k
∈ A
k
do
if <chấp nhận a
k
> then
Begin
x
k
:=a
k
;
if n = k then

<Cập nhật kỷ lục>
else
if
( )
1 2 k
, , ,g a a a


f
then
Try(k+1)
End;
End;
Khi đó thuật toán nhánh cận được thực hiện nhờ thủ tục sau:
Procedure Nhanh_can;
Begin

f
= +∞;
(* Nếu biết một phương án
x
nào đó thì có thể đặt
f
=
( )
f x
*)
Try(1);
if
f

< +∞ then
<
f
là giá trị tối ưu,
x
là phương án tối ưu>
else
<Bài toán không có phương án>
End;
9
CHƯƠNG 2
CHƯƠNG TRÌNH CHUYÊN SÂU MÔN TIN HỌC
TRƯỜNG THPT CHUYÊN
2.1 CHƯƠNG TRÌNH CHUYÊN SÂU MÔN TIN HỌC KHỐI 10
2.1.1 Mục đích
2.1.2 Kế hoạch và nội dung dạy học
2.2 CHƯƠNG TRÌNH CHUYÊN SÂU MÔN TIN HỌC KHỐI 11
2.2.1 Mục đích
2.2.2 Kế hoạch và nội dung dạy học
2.3 CHƯƠNG TRÌNH CHUYÊN SÂU MÔN TIN HỌC KHỐI 12
2.3.1 Mục đích
2.3.2 Kế hoạch và nội dung dạy học
2.4 ĐỊNH HƯỚNG PHƯƠNG PHÁP DẠY HỌC CHUYÊN ĐỀ
“PHƯƠNG PHÁP NHÁNH CẬN”
2.4.1 Tiến trình dạy và học trên lớp
Với chuyên đề thì giáo viên trình bày cho học sinh trong
khoảng thời lượng là 8 tiết (02 tiết dạy lý thuyết, 06 tiết cho học sinh
giải các bài toán được trình bày trong chương 3).
Hai tiết lý thuyết giáo viên nhắc lại thuật toán đệ quy quay lui,
trình bày phương pháp nhánh cận. Giáo viên cần đưa ra ví dụ cụ thể

để học sinh năm bắt được kiến thức thông qua ví dụ.
Khi dạy học sinh lập trình giải các bài toán đưa ra ở chương 3
giáo viên cho học sinh thực hiện quy trình như sau:
- Phát biểu bài toán;
- Học sinh xây dựng thuật toán và cài đặt chương trình trong
một lượng thời gian cụ thể (do giáo viên quy định theo trình độ học
sinh);
10
- Giáo viên chấm bài của học sinh bằng cách tạo ra những bộ
dữ liệu vào và đối sánh kết quả giữa giáo viên và học sinh;
- Nhận xét bài làm của học sinh sau đó trình bày thuật toán
nhánh cận để giải bài toán đặt ra.
- Yêu cầu học sinh cài đặt lại chương trình và tiếp tục chấm
kiểm tra kết quả của học sinh sau khi hoàn thành chương trình.
2.4.2 Tiến trình tự học của học sinh
Ngoài việc học ở lớp học sinh còn có thể tự học phương pháp
nhánh cận thông qua tài liệu và đánh giá chương trình của mình bằng
các bộ dữ liệu vào ra của giáo viên hoặc tự tạo ra các bộ dữ liệu để
đối sánh kết quả khi thực hiện chương trình của mình và chương
trình mẫu của giáo viên.
Kiểm tra bằng các bộ dữ liệu vào ra của giáo viên: Với các bộ
dữ liệu vào giáo viên đã tạo sẵn, học sinh chạy chương trình của
mình để sinh các bộ dữ liệu ra tương ứng. So sánh các bộ dữ liệu ra
của mình với các bộ dữ liệu ra của giáo viên, nếu kết quả giống nhau
thì chương trình cài đặt hoàn thành, còn nếu một bộ dữ liệu ra nào đó
không giống thì cần cải tiến lại chương trình.
Học sinh cũng có thể tạo ra các bộ dữ liệu vào theo yêu cầu
của bài toán, thực hiện chương trình của mình để tạo các bộ dữ liệu
ra, đồng thời cũng thực hiện chương trình mẫu của giáo viên để tạo
các bộ dữ liệu ra tương ứng. Đối sánh kết quả để kiểm chứng chương

trình của mình cài đặt.
11
CHƯƠNG 3
SỬ DỤNG PHƯƠNG PHÁP NHÁNH CẬN GIẢI MỘT SỐ
BÀI TOÁN TIÊU BIỂU
3.1 BÀI TOÁN NGƯỜI DU LỊCH
3.1.1 Phát biểu bài toán
Cho n thành phố được đánh số từ 1 đến n và m tuyến đường
giao thông hai chiều nối giữa các thành phố đó. Mạng lưới giao
thông được cho bởi bảng C cấp n×n, C[i, j] = C[j, i] là chi phí phải
trả khi đi đoạn đường trực tiếp từ thành phố i đến thành phố j. Giả
thiết rằng C[i, i] = 0 ∀i, C[i, j] = + ∞ nếu không có đường đi trực tiếp
từ thành phố i đến thành phố j.
Một người khách du lịch xuất phát từ thành phố 1, muốn đi
thăm tất cả các thành phố còn lại mỗi thành phố đúng một lần và
cuối cùng quay lại thành phố 1.
Yêu cầu: Hãy chỉ ra một hành trình du lịch sao cho chi phí
phải trả là ít nhất.
3.1.2 Thuật toán
Trong bài toán người du lịch khi tiến hành tìm kiếm lời giải ta
sẽ phân tập hành trình thành hai tập con: Một tập chứa cạnh (i,j) và
tập không chứa cạnh này. Ta gọi việc đó là phân nhánh, mỗi tập con
nói trên gọi là nhánh.
Việc phân nhánh sẽ được dựa trên qui tắc hợp lý nào đó cho
phép ta rút ngắn quá trình tìm kiếm phương án tối ưu. Sau khi phân
nhánh ta sẽ tính cận dưới của hàm mục tiêu trong mỗi tập con nói
trên. Việc tìm kiếm sẽ tìm trên tập con có cận dưới nhỏ hơn. Thủ tục
sẽ tiếp tục cho đến khi thu được hành trình đầy đủ, tức là phương án
của bài toán người du lịch. Sau đó ta chỉ cần xét những tập con có
12

cận dưới nhỏ hơn giá trị hàm mục tiêu tìm được.
Kỹ thuật tính cận dưới dựa trên thủ tục rút gọn sau:
a. Thủ tục rút gọn:
Rõ ràng tổng chi phí của một hành trình sẽ chứa đúng một
phần tử trên mỗi dòng và mỗi cột của ma trận chi phí C = (c
ij
). Do
đó nếu ta trừ bớt mỗi phần tử của một dòng (hay một cột) đi cùng
một giá trị thì chi phí của tất cả hành trình sẽ giảm đi một lượng, vì
thế hành trình tối ưu sẽ không thay đổi. Vì vậy nếu tiến hành trừ bớt
các phần tử của mỗi dòng và mỗi cột đi một hằng số sao cho thu
được ma trân không âm và mỗi cột cũng như mỗi dòng chứa ít nhất
một số 0, thì tổng các hằng số trừ đi đó sẽ cho ta cận dưới của mọi
hành trình. Thủ tục trừ bớt này gọi là thủ tục rút gọn, các hằng số trừ
ở mỗi dòng (cột) gọi là hằng số rút gọn dòng (cột), ma trận thu được
gọi là ma trận rút gọn.
Thủ tục rút gọn:
+ Đầu vào : Ma trận chi phí C = (c
ij
)
+ Đầu ra : Ma trận rút gọn và tổng hằng số rút gọn Sum
+ Thuật toán:
i) Khởi tạo :
Sum := 0 ;
(ii) Rút gọn dòng :
Với mỗi dòng r từ 1 đến n của ma trận C thực hiện:
- Tìm phần tử c
rj
= α nhỏ nhất trên dòng.
- Trừ tất cả các phần tử trên dòng đi một lượng α.

- Cộng dồn : Sum := Sum + α
(iii) Rút gọn cột :
Với mỗi cột c từ 1 đến n của ma trận C thực hiện :
- Tìm phần tử c
ic
= α nhỏ nhất trên cột.
13
- Trừ tất cả các phần tử trên cột đi một lượng α.
- Cộng dồn : Sum := Sum + α
c. Thủ tục chọn cạnh phân nhánh
Một cách lôgic là ta chọn cạnh phân nhánh (r,s) sao cho cận
dưới của nhánh không chứa (r,s) sẽ tăng nhiều nhất.
Thủ tục chọn cạnh phân nhánh (r,s)
+ Đầu vào : Ma trận rút gọn bậc k
+ Đầu ra : Cạnh phân nhánh (r,s).
+ Thuật toán :
(i) Khởi tạo : α := - ∞;
(ii) Với mỗi cặp (i,j) thoả c
ij
= 0 (i=1, ,k; j=1, ,k) thực hiện
- Xác định minr = min{c
ih
: h ≠ j }
mins = min{c
hj
: h ≠ i }
- Nếu α < minr + mins , đặt
α := minr + mins; r := i; s := j;
3.1.3 Độ phức tạp của thuật toán
Gọi T(n) là độ phức tạp tính toán thời gian của bài toán TSP

Bài toán thực hiện N-2 lệnh gọi TSP. Mỗi TSP có hai thủ tục
chính là Reduce và BestEdge.
Vì 2 thủ tục này thực hiện ở nhánh khác nhau. Do vậy, độ
phức tạp tính toán thời gian của mỗi TSP là:
Áp dụng công thức max, ta có:
T
TSP
= Max(T
Reduce,
T
BestEdge
) = Max(O(n
2
), O(n
3
)) = O(n
3
).
Vậy độ phức tạp tính toán thời gian của bài toán TSP là:
O(K * N * N
3
) K là số lần có thể phân nhánh, 0 <= K <= N
2
3.1.4 Cài đặt thuật toán
Chương trình được cài đặt hoàn thành bằng ngôn ngữ lập trình
Free Pascal bao gồm file chương trình nguồn (TOURISM.PAS), file
14
dữ liệu vào (TOURISM.INP), và file dữ liệu ra (TOURISM.OUT).
Các file dữ liệu vào (*.INP) và file dữ liệu ra (*.OUT) bao gồm 20
bộ dùng để kiểm tra chương trình đã được cài đặt, đồng thời các bộ

dữ liệu này cũng dùng để kiểm tra chương trình cài đặt của học sinh
trong quá trình dạy - học.
3.2. BÀI TOÁN XẾP BA LÔ
3.2.1. Phát biểu bài toán
Bài toán xếp ba lô (Knapsack):
Cho n sản phẩm, sản phẩm thứ i có trọng lượng là w
i
và giá trị
là v
i
(w
i
, v
i
∈ R
+
). Cho một ba lô có giới hạn trọng lượng là m.
Yêu cầu: Hãy chọn ra một số sản phẩm cho vào ba lô sao cho
tổng trọng lượng của chúng không vượt quá m và tổng giá trị của
chúng là lớn nhất có thể.
Dưới đây ta sẽ xây dựng thuật toán nhánh cận để giải bài toán
ba lô.
3.2.2. Thuật toán
Xây dựng thuật toán giải bài toán ba lô:
Obj[i] là lưu trọng số , giá trị và vị trí ban đầu của phần tử thứ i.
X[i] = True ⇔ Phần tử i được chọn trong lần xét hiện tại.
Best[i] = True ⇔ Phần tử i được chọn trong trường hợp tốt nhất.
SumW: Tổng trọng lượng trong lần xét hiện tại.
SumV: Tổng giá trị trong lần xét hiện tại
MaxV: Tổng giá trị trong lần xét tốt nhất.

Ban đầu: SumW := 0;
SumV := 0;
MaxV := -1;
Sắp xếp các v
i
/w
i
giảm dần.
15
Ta xây dựng hàm UpperBound(k, m): ước lượng xem nếu
chọn trong các sản phẩm từ k tới n với giới hạn trọng lượng m thì
tổng giá trị thu được không thể vượt quá bao nhiêu? Giá trị hàm
UpperBound(k, m) được tính như sau:
Function UpperBound(k: Integer; m: Real): Real;
Var i: Integer; q: Real;
Begin
Result := 0;
For i := k to n do //Xét các sản phẩm từ k tới n
Begin
if w[i] ≤ m then
q := w[i] //Lấy toàn bộ sản phẩm
Else
q := m; //Lấy một phần sản phẩm cho vừa đủ giới hạn
trọng lượng
Result := Result + q / w[i] * v[i]; //Cập nhật tổng giá trị
m := m – q; //Cập nhật giới hạn trọng lượng mới
if m = 0 then Break;
End;
End;
Mỗi khi chuẩn bị ra quyết định chọn hay không chọn sản phẩm

thứ k, thuật toán cần dựa vào tổng giá trị các sản phẩm đã quyết định
chọn trước đó và giá trị hàm UpperBound để ước lượng cận trên của
tổng giá trị có thể thu được. Nếu thấy không còn cơ may tìm ra
phương án tốt hơn phương án đang được ghi nhận, quá trình quay lui
sẽ thực hiện việc “tỉa nhánh”: không thử chọn trong các sản phẩm từ k
tới n nữa bởi nếu muốn tìm ra phương án tốt hơn, cần phải thay đổi
các quyết định chọn trên các sản phẩm từ 1 tới k - 1.
16
+ Đánh giá tương quan giữa các phần tử của cấu hình:
Với mỗi sản phẩm, đôi khi ta không cần phải thử hai khả năng:
chọn/không chọn. Một trong những cách làm là dựa vào những quyết
định trên các sản phẩm trước đó để xác định sớm những sản phẩm
chắc chắn không được chọn.
Giả sử trong số n sản phẩm đã cho có hai sản phẩm: sản phẩm
A có trọng lượng 1 và giá trị 4, sản phẩm B có trọng lượng 2 và giá
trị 3. Rõ ràng khi sắp xếp theo mật độ giảm dần thì sản phẩm A sẽ
đứng trước sản phẩm B và sản phẩm A sẽ được thử trước, và khi đó
nếu sản phẩm A đã không được chọn thì sau này không có lý do gì ta
lại chọn sản phẩm B.
Tổng quát hơn, ta sẽ lập tức đưa quyết định không chọn sản
phẩm k nếu trước đó ta đã không chọn sản phẩm i (i < k) có w
i
≤ w
k
và v
i
≥ v
k
.
Nhận xét này cho ta thêm một tiêu chuẩn để hạn chế không

gian duyệt. Tiêu chuẩn này có thể viết bằng hàm Selectable(j, k), (j <
k): Cho biết có thể nào chọn sản phẩm k trong điều kiện ta đã quyết
định chọn hay không chọn trên các sản phẩm từ 1 tới j:
Function Selectable(j, k: Integer): Boolean;
Var i: Integer;
Begin
For i := 1 to j do
If not x[i] and (w[i] ≤ w[k]) and (v[i] ≥ v[k]) //Sản
phẩm i không được chọn và i "không tồi hơn" k then
Exit(False); //Kết luận k chắc chắn không được chọn
Result := True;
End;
17
Hàm Selectable sẽ được dùng trong thủ tục quay lui, đồng thời
tích hợp vào hàm UpperBound để có một đánh giá cận chặt hơn.
Thủ tục chính để chọn phần tử thứ i đưa vào ba lô.
Procedure Attempt(i: Integer);
Begin
// Ước lượng xem các phần tử từ i đến n có vượt quá kết quả tốt nhất
hiện tại hay không.
if SumV + UpperBound(i, m - SumW) <= MaxV then Exit;
// Duyệt xong n phần tử, lưu trạng thái tốt nhất.
if i = n + 1 then Begin Best := x; MaxV :=SumV; Exit; End;
// Nếu phần tử i được chọn
if (SumW + obj[i].w <= m) and Selectable(i - 1, i) then
Begin
X[i] := True; // Đánh dấu phần tử i được chọn
SumW := SumW + obj[i].w; // Tổng trọng lượng trong lần xét hiện
tại
SumV := SumV + obj[i].v; // Tổng giá trị trong lần xét hiện tại

Attempt(i + 1); // Đệ quy tìm cách chọn phần từ tiếp theo
SumW := SumW - obj[i].w; //Loại bỏ trọng lượng của phần tử i
SumV := SumV - obj[i].v; // Loại bỏ giá trị của phần tử i
X[i] := False;
End;
End;
3.2.3 Độ phức tạp của thuật toán
Gọi F(i) là độ phức tạp tính toán thời gian của hàm
Selectable(i, k). F(i) = O(i);
Gọi G(i) là độ phức tạp tính toán thời gian của hàm
UpperBound(i, m). Hàm này thực hiện N-i+1 lần lặp.
18
Gọi là G’(i) là độ phức tạp tính toán thời gian số lần lặp này.
G’(i) = O(N-i+1)
Lần lặp k gọi hàm Selectable(i-1, k)
Theo nguyên lý nhân, ta có: G(i) = O(F(i)*G’(i)) = O(N-i)
Gọi T(i) là độ phức tạp tính toán thời gian của lần gọi
Attempt(i). Theo nguyên lý lấy max, ta có:
T(i) = Max(F(i), G(i)) = O(N-i).
Gọi T là độ phức tạp tính toàn thời gian của bài toán.
Theo nguyên lý nhân, ta có:
T = T(1) * T(2) * * T(N) = O(N-1) * O(N-2) * * O(1) = O(N!)
Vậy độ phức tạp của thuật toán là: O(N!)
3.2.4 Cài đặt thuật toán
Chương trình được cài đặt hoàn thành bằng ngôn ngữ lập trình
Free Pascal bao gồm file chương trình nguồn (KNAPSACK.PAS),
file dữ liệu vào (KNAPSACK.INP), và file dữ liệu ra
(KNAPSACK.OUT). Các file dữ liệu vào (*.INP) và file dữ liệu ra
(*.OUT) bao gồm 20 bộ dùng để kiểm tra chương trình đã được cài
đặt, đồng thời các bộ dữ liệu này cũng dùng để kiểm tra chương trình

cài đặt của học sinh trong quá trình dạy - học.
3.3. BÀI TOÁN ĐỒ THỊ CON ĐẦY ĐỦ CỰC ĐẠI
3.3.1 Phát biểu bài toán
Định nghĩa: Đồ thị đầy đủ có hướng là đơn đồ thị mà hai
đỉnh bất kỳ được nối với nhau bằng một cung (theo chiều nào cũng
được). Đồ thị đầy đủ vô hướng là đơn đồ thị mà hai đỉnh bất kỳ
được nối với nhau bằng một cạnh. Ta có số cạnh của đồ thị đầy đủ
vô hướng với N đỉnh là N*(N-1)/2 cạnh.
19
Bài toán tìm đồ thị con đầy đủ cực đại là một bài toán có rất
nhiều ứng dụng trong các mạng xã hội, tin sinh học, mạng truyền
thông, nghiên cứu cấu trúc phân tử…
Ta có thể phát biểu bài toán như sau:
Có n người và mỗi người có quen biết một số người khác. Giả
sử quan hệ quen biết là quan hệ hai chiều, tức là nếu người i quen
người j thì người j cũng quen người i và ngược lại.
Yêu cầu: Hãy chọn ra một tập gồm nhiều người nhất trong số
n người đã cho để hai người bất kỳ được chọn phải quen biết nhau.
3.3.2 Thuật toán
Ta xây dựng thuật toán như sau:
A[i, j] = True ⇔ Người i và người j có quan biết với nhau.
deg[i] là số người quen của người i
count[i] là số người quen của người i mà đã được chọn.
X[i] = True ⇔ Người i được chọn trong lần xét hiện tại.
best[i] = True ⇔ Người i được chọn trong kết quả tốt nhất.
k số người được chọn trong lần xét hiện tại.
kbest là số người được chọn trong kết quả tốt nhất.
Các quan hệ quen biết nhau được biểu diễn bởi ma trận A =
{a
ij

}
n
×
n
trong đó a
ij
= True nếu như người i quen người j và a
ij
= False
nếu như người i không quen người j. Theo giả thiết của bài toán, ma
trận A là ma trận đối xứng: a
ij
= a
ji
(∀i, j).
Rõ ràng với một người bất kỳ thì có hai khả năng: người đó
được chọn hoặc người đó không được chọn. Vì vậy một nghiệm của
bài toán có thể biểu diễn bởi dãy X = (x
1
, x
2
, …, x
n
) trong đó x
i
= True
nếu người thứ i được chọn và x
i
= False nếu người thứ i không được
chọn. Gọi k là số người được chọn tương ứng với dãy X, tức là số vị

trí x
i
= True.
20
Phương án tối ưu được lưu trữ bởi mảng best[1 n] với kbest
là số người được chọn tương ứng với dãy best. Để đơn giản, ta khởi
tạo mảng best[1 n] bởi giá trị False và kbest := 0, sau đó phương án
best và biến kbest sẽ được thay bằng những phương án tốt hơn trong
quá trình duyệt.
Mô hình duyệt được thiết kế như mô hình liệt kê các dãy nhị
phân bằng thuật toán quay lui: Thử hai giá trị True/False cho x
1
, với
mỗi giá trị vừa thử cho x
1
lại thử hai giá trị của x
2

Giá trị deg[i] được xác định ngay từ đầu còn giá trị count[i]
sẽ được cập nhật ngay lập tức mỗi khi ta thử quyết định chọn hay
không chọn một người j quen với người i (j < i). Mảng deg[1 n] và
count[1 n] được sử dụng trong hàm cận để hạn chế bớt không gian
duyệt.
+ Hàm cận:
Thuật toán nhánh cận được thực hiện thông qua thử tục
Attempt(i): Thử hai giá trị có thể gán cho x
i
. Thuật toán được bắt đầu
bằng thủ tục Attempt(1) và khi thủ tục Attempt(i) được gọi thì ta đang
có một phương án chọn trên tập những người từ 1 tới i - 1 và số

người đang được chọn trong tập này là k.
Trong những người từ i tới N, chắc chắn nếu có chọn thêm
thì ta chỉ được phép chọn những người j mà count[j] ≥ k và deg[j] >
kbest. Điều này ta có thể dễ nhìn thấy được: count[j] < k có nghĩa là
người j không quen với ít nhất một người đã chọn; còn deg[j] ≤
kbest có nghĩa là nếu người j được chọn, phương án tìm được chắc
chắn không thể có nhiều hơn kbest người, không tốt hơn phương án
best hiện có.
* Duyệt kiểm tra xem người thứ i có được chọn trong lần xét
hiện tại hay không, ta xây dựng thủ tục sau:
21
Procedure Attempt(i: Integer);
Var j: Integer;
Begin
// Kiểm tra xem có bao nhiêu người được chọn từ người i đến
người thứ n.
// Nếu số người nhỏ hơn kết quả tốt nhất thì không xét tiếp nữa.
if UpperBound(i) <= kbest then Exit;
// Nếu đã xét xong n người thì cập nhật kết quả.
if i = n + 1 then
Begin
best := x;
kbest := k;
Exit;
End;
// Nếu i được chọn
if (count[i] >= k) and (deg[i] > kbest) then
Begin
X[i] := True; // Đánh dấu người i được chọn
Inc(k); // Tăng số lượng người được chọn

// Tăng số lượng người quen của các người có quen người i
For j := i + 1 to n do
if a[i, j] then Inc(count[j]);
Attempt(i + 1); // Duyệt kiểm tra người tiếp theo
X[i] := False; // Loại bỏ người i ra khỏi kết quả hiện tại
Dec(k);
// Giảm số lượng người được chọn
// Giảm số lượng người quen của các người có quen người i
For j := i + 1 to n do
22
if a[i, j] then Dec(count[j]);
End;
End;
3.3.3 Độ phức tạp của thuật toán
Từ thuật toán nêu trên ta đi đi tìm độ phức tạp tính toán của
bài toán như sau:
Lần gọi chương trình con Attempt(1): Có N-1 cách chọn.
Lần gọi chương trình con Attempt(2): Có N-2 cách chọn.
Lần gọi chương trình con Attempt(i): Có N-i+1 cách chọn.
Lần gọi chương trình con Attempt(N): Có 1 cách chọn.
Gọi T(i) là độ phức tạp tính toán thời gian ở lần gọi
Attempt(i). T(i) = N – i + 1;
Gọi T là độ phức tạp tính toán thời gian của bài toán.
Theo nguyên lý nhân, ta có:
T = T(2) * T(3) * * T(N) = (N – 1) * (N – 2) * * 1 = (N – 1)!
Vậy độ phức tạp tính toán thời gian của bài toán là O((N-1)!)
3.3.4 Cài đặt thuật toán
Chương trình được cài đặt hoàn thành bằng ngôn ngữ lập trình
Free Pascal bao gồm file chương trình nguồn (SUBGRAPH.PAS),
file dữ liệu vào (SUBGRAPH.INP), và file dữ liệu ra

(SUBGRAPH.OUT). Các file dữ liệu vào (*.INP) và file dữ liệu ra
(*.OUT) bao gồm 20 bộ dùng để kiểm tra chương trình đã được cài
đặt, đồng thời các bộ dữ liệu này cũng dùng để kiểm tra chương trình
cài đặt của học sinh trong quá trình dạy - học.
23
KẾT LUẬN
Trong luận văn này trình bày những kiến thức tổng quát về
cách giải bài toán bằng phương pháp nhánh cận. Luận văn đã đưa ra
được mô hình bài toán tổng quát, các bước xây dựng thuật toán, đưa
ra được sơ đồ chung cho thuật toán nhánh cận.
Luận văn đã tìm hiểu và phát biểu các bài toán có thể giải
được bằng phương pháp nhánh cận. Nghiên cứu và xây dựng thuật
toán cho một số bài toán cụ thể. Tiến hành cài đặt chương trình dựa
trên thuật toán đã xây dựng, Test chương trình cài đặt bằng các bộ dữ
dữ liệu vào ra hoàn chỉnh.
Giáo viên và học sinh nắm được phương pháp nhánh cận
thông qua việc khảo sát một số bài toán cụ thể, tiêu biểu. Học được
cách nghĩ, cách tiếp cận vấn đề, cách thiết kế giải thuật và rèn luyện
kỹ năng linh hoạt khi giải các bài toán.
Trên cơ sở phát triển thuật toán nhánh cận tôi sẽ mở rộng và
nghiên cứu các thuật toán để giải cho các lớp bài toán khác như thuật
toán Vét cạn; Chia để trị; Quy hoạch động; Tham lam; Các thuật
toán trên đồ thị; Các thuật toán trên Cây… để nâng cao chất lượng
cũng như hiệu quả học tập của học sinh.
24

×