Phần 1: ĐĂT VÂN ĐÊ
̣
́
̀
Kinh th
́
ưa quy ́thây cô giao!
̀
́
Tiến sĩ triều Lê, Thân Nhân Trung đã nói “Hiền tài là nguyên khí
của quốc gia, nguyên khí thịnh thì thế nước mạnh mà hưng thịnh, nguyên
khí suy thì thế nước yếu mà thấp hèn”. Vì thế các bậc đế vương thánh
minh luôn coi việc giáo dục nhân tài, kén chọn kẻ sĩ, vun trồng nguyên
khí quốc gia làm công việc hàng đầu..., câu nói bất hủ của Thân Nhân
Trung đã cho thấy từ thời xa xưa các thế hệ ông cha đã rất coi trọng nhân
tài và coi những nhân tài là tương lai của đất nước. Với cương vị là một
giáo viên chuyên ngành Tin học trực tiếp giảng dạy, tôi thấy được những
nhiệm vụ quan trọng phải làm đầu tiên đó là làm thế nào để học sinh
thích học và học giỏi môn Tin. Trong thời đại ngày nay, Tin học có vai
trò và vị trí đặc biệt quan trọng trong khoa học kĩ thuật và đời sống, giúp
con người tiếp thu một cách dễ dàng các môn khoa học khác có hiệu
quả.
Có thể nói phát hiện và bồi dưỡng HSG là một trong những hoạt
động chuyên môn chính trong năm học của trường. Bản thân qua tham
khảo các đề thi HSG của tỉnh và quốc gia đã thấy hầu hết các đề thi đều
có các bài toán tối ưu. Để giải các bài toán tôi ưu thì có nhiều phương
pháp giải, trong đó ứng dụng thuật toán quay lui để giải bài toán tối ưu
cũng là một phương pháp được nhiều người sử dụng. Đo cung chinh la
́ ̃
́
̀
ly do đê tôi viêt đê tai
́
̉
́ ̀ ̀ “Thuật toán quay lui và ứng dụng giải bài toán
tối ưu”.
Tôi rât mong đ
́
ược sự gop y cua quy thây cô đê đê tai ngay cang
́ ́ ̉
́ ̀
̉ ̀ ̀
̀ ̀
được hoan thiên h
̀
̣ ơn.
Xin chân thanh cam
̀
̉ ơn!
Trang 1
Phân 2: NH
̀
ỮNG BIÊN PHAP GIAI QUYÊT VÂN ĐÊ
̣
́
̉
́
́
̀
I. CƠ SỞ LÝ LUẬN CỦA VẤN ĐỀ:
Cũng như trình bày ở trên bài toán tối ưu là một trong những bài
toán thường gặp trong các kỳ thi học sinh giỏi.
Công việc khó khăn ở đây là làm thế nào để các em có thể hiểu
được thuật toán và có thể ứng dụng vào giải các bài toán liên quan.
Xuất phát từ thực tiễn như vậy, tôi đã đưa ra một số bước giúp cho
các em có thể hiểu được thuật toán và ứng dụng để giải các bài toán
cùng loại:
Ý tưởng của thuật toán.
Thuật toán.
Ứng dụng thuật toán giải các bài toán đơn giản.
Ứng dụng thuật toán quay lui có đánh giá cận giải một số bài
toán phức tạp.
Ứng dụng thuật toán giải bài toán trong các đề thi học sinh giỏi.
II. THỰC TRẠNG CỦA VẤN ĐỀ:
1) Thực trạng về cấp quản lý
*) Ưu điểm:
Đã quan tâm vào công tác phát triển mũi nhọn.
Có sự phân công nhiệm vụ cho từng giáo viên trực tiếp
giảng dạy, giám sát và kiểm tra quá trình thực hiện của giáo viên.
Động viên tinh thần cũng như tạo mọi điều kiện thuận lợi
nhất để giáo viên thực hiện nhiệm vụ.
*) Hạn chế:
Khi phân công nhiệm vụ thì chưa xác định một chiến lược
lâu dài, đó là chỉ tập trung phát hiện và bỗi dưỡng học sinh lớp 11
Trang 2
mà không phát hiện và bồi dưỡng ngay từ khi các em đang học lớp
10.
2) Thực trạng về giáo viên
*) Ưu điểm:
Được đào tạo về chuyên môn cơ bản, có sức khỏe, sức trẻ,
có lòng nhiệt tình trong công việc. Luôn luôn học tập trau dồi tri
thức, nhằm phục vụ tốt nhất cho sự nghiệp giáo dục.
Trong quá trình giảng dạy, tuy gặp nhiều khó khăn nhưng
phần lớn các thầy cô giáo đều đặt chữ “tâm” lên hàng đầu, đây là
một trong những thuận lợi góp phần vào sự thành công của ngành
giáo dục.
Có sự đầu tư vào nghiên cứu khi được giao nhiệm vụ.
*) Hạn chế:
Một số giáo viên không có tâm huyết, chưa tập trung vào
công tác chuyên môn nên kiến thức về ôn luyện học sinh giỏi còn
hạn chế. Việc bồi dưỡng HSG chỉ tập trung cho số ít giáo viên
trong tổ.
3) Thực trạng về học sinh
*) Ưu điểm:
Các em HS ngoan, cần cù chịu khó, có ý thức vươn lên
trong học tập.
Có trách nhiệm với việc học tập, trong quá trình học tập
hăng say phát biểu, đóng góp nên sự thành công của bài giảng.
*) Hạn chế:
Kiến thức cơ bản về lập trình còn rất nhiều hạn chế, chỉ
có số ít em học sinh trên địa bàn thị trấn, mới được tiếp cận với
lập trình ở chương trình lớp 8 – THCS.
Trang 3
Đa số các em học tốt môn Tin học thì lại học tốt các môn
khoa học tự nhiên (Toán, Lí, Hóa), nên đã theo tham gia bồi dưỡng
ở các bộ môn đó.
Tin học là môn mà không có trong các kỳ thi tốt nghiệp
cũng như đại học hàng năm, nên các em cũng không mặn mà với
việc tham gia học và bồi dưỡng HSG.
4) Thực trạng về cơ sở vật chất
*) Ưu điểm:
Có đủ cơ sở vật chất để phục vụ cho lớp học.
Có các phương tiện phục vụ cho mục đích giảng dạy , VD:
Bảng từ, máy chiếu, máy tính...
*) Hạn chế:
Thiếu tài liệu và sách tham khảo.
Một số học sinh không có đủ kinh phí mua máy tính cá
nhân phục vụ cho mục đích ôn tập tại nhà.
III. CÁC BIỆN PHÁP ĐÃ TIẾN HÀNH ĐỂ GIẢI QUYẾT VẤN ĐỀ:
1. Thuật toán quay lui: Giả thiết một cấu hình cần tìm được mô tả bởi
một bộ phận gồm n thành phần a1, a2,...an. Giả sử tìm được i 1 thành
phần a1, a2, ai1, ta tìm thành phần thứ i bằng cách duyệt tất cả các khả
năng có thể của ai. Với mỗi khả năng j kiểm tra xem nó có chấp nhận
được không. Xảy ra hai trường hợp:
Chấp nhận được thì xác định ai theo j và kiểm tra xem i = n chưa, nếu i
= n thì ta ghi nhận một cấu hình, còn nếu i
Nếu thử tất cả các khả năng mà không có khả năng nào chấp nhận
được thì quay lại bước trước xác định lại ai1.
Trang 4
Nội dung của thuật toán này rất phù hợp với việc gọi đệ quy. Ta có thủ
tục đệ quy sau đây:
Procedure Try (i:tinteger);
Var j:integer;
Begin
For j:=1 to n do
if chấp nhận j then
Begin
xác nhận ai theo j
if i=n then <Ghi nhận một cấu hình>
Else try(i+1);
End;
End;
2. Ứng dụng thuật toán quay lui giải một số bài toán cơ bản:
Bài 1: Hãy viết chương trình liệt kê tất cả các dãy nhị phân có độ dài n.
Bài 2: Hãy viết chương trình liệt kê các hoán vị của {1,2,...,n}
Bài 3: Hãy viết chương trình liệt kê các tổ hợp chập m của {1,2,...,n}
Bài 4: Liệt kê tất cả các cách sắp xếp những con hậu trên bàn cờ NxN
sao cho chúng không ăn được nhau.
Bài 5: Hãy viết chương trình liệt kê tất cả các chu trình Haminton của
đơn đồ thị vô hướng. (Chu trình bắt đầu từ đỉnh v nào đó qua tất cả các
đỉnh còn lại, mỗi đỉnh đúng một lần rồi quay trở về đỉnh v được gọi là
chu trình Hamilton).
(*Bài 1: Hãy viết chương trình liệt kê tất cả các dãy nhị phân có độ
dài n.*)
Trang 5
Giải: Biểu diễn dãy nhị phân dưới dạng b1,b2,...,bn trong đó bi {0,1}.
Thủ tục đệ quy Try(i) xác định bi, trong đó các giá trị đề cử là 0 và 1. Các
giá trị này mặc nhiên được chấp nhận mà không phải thỏa mãn điều
kiện gì (vì vậy bài toán không cần biến trạng thái). Thủ tục Init nhập giá
trị n và khởi gán biến đếm Count. Thủ tục result đưa ra dãy tìm được.
Program Day_nhi_phan;
Uses Crt;
Var b:array[1..20] of 0..1;
n:Integer; count:byte;
Procedure Init;
Begin
Write('Nhap n='); readln(n);
Count:=0;
End;
Procedure Result;
Var i:integer;
Begin
Count:=count+1; Write(Count:5,'.');
For i:=1 to n do write(b[i]:3); writeln;
End;
Procedure Try(i:integer);
Var j:integer;
Begin
For j:=0 to 1 do
Begin
b[i]:=j;
Trang 6
if i=n then result Else try(i+1);
End;
End;
BEGIN
Clrscr;
Init;
try(1);
Readln;
END.
(*Bài 2: Hãy viết chương trình liệt kê các hoán vị của {1,2,...,n}*)
Giải: Biểu diễn hoán vị dưới dạng p1,p2,...,pn, trong đó pi nhận các giá trị
từ 1 đến n và pi pj với i j. Các giá trị từ 1 đến n lần lượt được đề cử
cho pi, trong đó giá trị j được chấp nhận nếu nó chưa được dùng. Vì vậy
cần được ghi nhớ đối với mỗi giá trị j xem nó đã được dùng hay chưa.
Điều này được thực hiện nhờ một dãy biến logic bj, trong đó bj bằng
True nếu j chưa được dùng. Các biến này cần được gán giá trị True trong
thủ tục Init. Sau khi gán j cho pi cần ghi nhận False cho bj và phải gán lại
giá trị True sau khi thực hiện xong Result.
Program Liet_ke_hoan_vi;
Uses Crt;
Var n:Integer;
p:array[1..20] of integer;
b:array[1..20] of boolean;
Count:Word;
Procedure Init;
Trang 7
Var i:Integer;
Begin
Write('Nhap n='); readln(n);
For i:=1 to n do b[i]:=true;
count:=0;
End;
Procedure Result;
Var i:integer;
Begin
Count:=count+1;
Write(Count:5,'.');
For i:=1 to n do write(p[i]:3);
Writeln;
End;
Procedure Try(i:integer);
Var j:integer;
Begin
For j:=1 to n do
if b[j] then
Begin
p[i]:=j;
b[j]:=false;
if i=n then result Else try(i+1);
b[j]:=true;
End;
End;
Trang 8
BEGIN
Clrscr;
init;
try(1);
Readln;
END.
(* Bài 3: Hãy viết chương trình liệt kê các tổ hợp chập m của
{1,2,...,n}*)
Giải: Biểu diễn tổ hợp dưới dạng c1c2...cm, trong đó:
1 c1
c2
... cm
n
Từ đó suy ra các giá trị đề cử cho ci là từ ci1+1 đến n. Để điều này đúng
cho cả trường hợp i=1, cần thêm vào c0 với c0=0. Các giá trị đề cử này
mặc nhiên được chấp nhận. Các thủ tục Init, result được xây dựng giống
như bài toán trước.
Program Liet_ke_to_hop;
Uses Crt;
Var n,m:integer;
c:array[0..20] of integer;
count:word;
Procedure Init;
Begin
Write('n,m='); readln(n,m);
c[0]:=0; Count:=0;
End;
Trang 9
Procedure result;
Var i:integer;
Begin
Count:=count+1;
Write(Count:5,'.');
For i:=1 to m do write(c[i]:3);
writeln;
End;
Procedure Try(i:integer);
Var j:integer;
Begin
For j:=c[i1]+1 to n do
Begin
C[i]:=j;
if i=m then result Else try(i+1);
End;
End;
BEGIN
Clrscr;
Init; Try(1);
Write('Go Enter de ket thuc!');
Readln;
END.
(* Bài 4: Liệt kê tất cả các cách sắp xếp những con hậu trên bàn cờ
NxN sao cho chúng không ăn được nhau *)
Trang 10
Giải: Ta xếp n con hậu trên n dòng, Theo nguyên lý nhân ta có nn cách
sắp xếp thoả mãn điều kiện đầu bải. Để làm điều đó ta dùng thủ tục đệ
quy mô tả ở trên để giải. Ta đánh ghi số cột và dòng của bàn cờ từ 1 đến
n, mỗi cách sắp xếp ứng với 1 bộ gồm a1,a2,.....,an với ai = j (j=1,2,...,n)
có nghĩa là con hậu thứ i đặt vào cột j. Giả sử ta chọn được i1 con hậu
bằng cách duyệt tất cả các khả năng của nó.
Quan trọng nhất là ta tìm điều kiện chấp nhận j, một con hậu đứng ở
một ô trong bàn cờ nó có nhiều nhất bốn hướng đi (đường dọc, đường
ngang và hai đường chéo).
Vậy điều kiện chấp nhận thứ i thoả mãn không nằm trên đường đi
của tất cả i1 con hậu đã xếp. Bởi vì n con hậu xếp ở n hàng nên đường
đi ngang của chúng là không chiếu nhau, do đó khi chọn con hậu thư i chỉ
cần kiểm tra xem trên 2 đường chéo và đường dọc của chúng có chiếu
vào những con hậu đã xếp không? Để kiểm tra điều này mỗi đường ta
dùng một biến trạng thái.
* Đường dọc kiểm soát bằng biến a[j],(j=1,2,...,n).
* Một đường chéo kiểm soát bằng biến b[i+j],i+j={2,....,2n}.
* Còn đường chéo kia kiểm soát bằng biến c[ij],ij={1n,....,n1}.
Các biến trạng thái này khởi gán giá trị True trong thủ tục Init. Như
vậy con hậu thứ i được chấp nhận xếp vào cột j nếu nó thoả mãn cả ba
biến a[j],b[i+j],c[ij] đều có giá trị true. Các biến này gán giá trị False khi
xếp xong con hậu thứ i, và trả lại giá trị true sau khi gọi Result hay
Try(i+1). Ta có chương trình Pascal sau:
Program XepHau;
Uses crt;
Var n : integer;
x:array[1..30] of integer;
Trang 11
a:array[1..30] of boolean;
b:array[2..60]of boolean;
c:array[19..19] of boolean;
count:word;
Procedure Init;
Var i:integer;
Begin
Write('Cho do rong ban co n= ');
Readln(n);
Count:=0;
For i:=1 to n do a[i]:=true;
For i:=2 to 2*n do b[i]:=true;
For i:=1n to n1 do c[i]:=true;
End;
Procedure Result;
Var i:integer;
Begin
count:=count+1;
Write('Cach xep thu',count:5,'.');
for i:=1 to n do write(a[i]:2); Writeln;
End;
Procedure try(i:integer);
Var j : integer;
Begin
For j:=1 to n do
If (a[j]) and (b[i + j]) and (c[i j]) then
Trang 12
Begin
x [i] : = j;
a [j] : = false;
b[i + j]: = false;
c [ij] : = false;
if i = n then Result Else try (i+1);
a [j] : = true;
b[i + j]: = true;
c[i j]: = true;
End;
End;
BEGIN
clrscr;
Init;
Try(1);
Write ('An Enter de ket thuc:');
Readln;
END.
(*Bài 5: Hãy viết chương trình liệt kê tất cả các chu trình Haminton
của đơn đồ thị vô hướng *)
Giải:
Program Chutrinhhamilton;
Const max=100;
var f:Text;
a:array[1..max, 1..max] of Boolean;
Chuaxet:array[1..max] of Boolean;
X:array[1..max] of Integer;
Trang 13
n:Integer;
procedure Enter;
var i,u,v,m: Integer;
Begin
FillChar(a,SizeOf(a),False);{tạo ra 1 mảng được gán, in ra là false}
ReadLn(f1,n,m);
for i:=1 to m do
begin
ReadLn(f1,u,v);
a[u,v]:=True; a[v,u]:=True;
end;
end;
procedure PrintResult;
var i:Integer;
begin
Writeln('CO TON TAI CHU TRINH EULER');
for i:=1 to n do Write(f2, X[i],'>');
WriteLn(f2,X[1]);
end;
procedure Try(i:Integer);
var j:Integer;
Begin
for j:=1 to n do
if Chuaxet[j] and a[x[i1],j] then
begin
x[i]:=j;
if i < n then
Trang 14
begin
Chuaxet[j]:= False;
Try(i+1);
Chuaxet[j]:= True;
end
else
if a[j,X[1]] then PrintResult;
end;
End;
BEGIN
Assign(F1, 'HAMILTON.INP'); Reset(F1);
Assign(F2, 'HAMILTON.OUT'); Rewrite(F2);
Enter;
FillChar(Chuaxet,n,True);
x[1]:=1; Chuaxet[1]:=False;
Try(2);
Close(F1);
Close(F2);
END.
Trang 15
3. Phương Pháp Nhánh Cận (Branch and Bound)
(Bài toán cái túi và bài toán người du lịch)
a. Nội dung:
Đặt vấn đề
Ý tưởng
Thuật giải
Cài đặt
Đánh giá
Ví dụ minh họa
b. Đặt vấn đề:
Bài toán thực tế: Bài toán người giao hàng.
Một người cần phải giao hàng tại N thành phố T1, T2, …, Tn.
Cij: chi phí đi từ thành phố Ti đến thành phố Tj (i=1,2,…,N; j = 1,2,
…,N).
Yêu cầu: xác định hành trình thỏa mãn đi qua tất cả các thành phố,
mỗi thành phố qua đúng 1 lần, rồi quay trở lại thành phố xuất
phát. Chi phí nhỏ nhất.
Cách 1 giải quyết bài toán:
Phương pháp vét cạn.
Phương pháp vét cạn quay lui .
Một số phương pháp khác.
Nhược điểm: Phải xét cả những phương án không khả thi (gây bùng
nổ tổ hợp khi dữ liệu đầu vào n lớn)
Cách 2 giải quyết bài toán:
Mô hình thành phố.
Trang 16
Sử dụng thuật toán quay lui.
c. Ý tưởng (cách 2):
Giữ lại 1 phương án mẫu.
Tính chi phí của các phương án khác ngay trong quá trình xây dựng.
Nếu tốt hơn: Cập nhật lại phương án mẫu và đi tiếp.
Không tốt hơn: Quay lại bước trên xét phương án khác.
d. Thuật giải:
Bài toán tối ưu: Tìm min{f(x): x D}
Khái niệm bài toán tối ưu: 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ị tốt nhất.
Nghiệm bài toán có dạng x= ( x1,x2,…,xn ).
Bước 1: Xuất phát từ x1, xây dựng một phương án mẫu f*
Bước i: Đã xây dựng được nghiệm thành phần (x1, x2,…, xi1)
Đánh giá cận: tìm g xác định trên xi:
g(x1,…,xi) < Min {f(a): a=(a1,…,an) thuộc x, xi=ai, i=1,…,n}
Giả sử x* là lời giải tốt nhất tại thời điểm đó, f* là giá trị tốt nhất
f*=f(x*)
Nếu f*
…,x i)
Ngược lại: tiến hành bước i+1 để xác định xi+1.
e. Cài đặt:
Try(i)
for ( j=1>n ) if ( chấp nhận được j) {Xác định xi theo j; Ghi nhận trạng
thái mới}
if ( i=n ) Cập nhật lời giải tối ưu
Else { Xác định cận g(x1,…,xi); if ( g(x1,…,xi) < f* ) Try(i+1)}}}
f. Đánh giá:
Trang 17
Ưu điểm: Giảm được chi phí: do loại bỏ được những bước đi không
cần thiết (nhờ đánh giá cận).
Nhược điểm:Việc xây dựng hàm g phụ thuộc vào từng bài toán tối ưu
tổ hợp cụ thể. Hàm g phải đảm bảo điều kiện:
Việc tính giá trị của g phải đơn giản hơn việc giải bài toán tổ hợp
tìm
min= min{f (a): a=(a1 ,…,an) thuộc x, xi =ai , i=1,…,n}
Giá trị của g(a1, a2 ,…, ak) phải sát với các giá trị của min.
g. Ví dụ minh họa:
Bài 1: Bài toán cái túi
Một nhà thám hiểm cần đem theo một cái túi có trọng lượng không
quá b. Có n đồ vât có thể mang theo (số lượng mỗi loại là không hạn
chế). Đồ vật thứ j có trọng lượng aj và giá trị sử dụng cj (j=1,2,...,n). Hỏi
nhà thám hiểm cần đem theo những loại đồ vật nào, số lượng mỗi loại
là bao nhiêu để cho tổng giá trị sử dụng của các đồ vật đem theo là lớn
nhất?
Mô hình toán học của bài toán có dạng sau: Tìm
f = max{ f ( x) =
n
cj.xj :
j =1
n
aj.xj
b, xj
Z + , j = 1, 2,..., n}.
j =1
Trong đó: xj là số lần lấy đồ vật j.
cj là giá trị đồ vật j.
aj là trọng lượng đồ vật j.
Dưới đây là chương trình pascal giải bài toán cái túi theo thuật toán
quay lui có đánh giá cận với các biến sử dụng trong chương trình là:
(* Cost: Tổng giá trị các đồ vật chứa trong túi tại thời điểm đang xét *)
(* Fopt: Giá trị kỷ lục đến tại thời điểm đang xét *)
(* Xopt: Mảng chứa số lần lấy các đồ vật đã bỏ vào túi *)
Trang 18
(* Weight: Trọng lượng của cái túi tại thời điểm đang xét *)
(* c[i]: Giá trị đồ vật thứ i *)
(* a[i]: Trọng lượng đồ vật thứ i *)
(* n: Số lượng đồ vật đã cho *)
(* w: Trọng lượng toàn bộ cái túi có thể chứa *)
(* ind(i): Ghi nhận vị trí ban đầu của đồ vật i *)
(* x[i]: Số lần lấy đồ vật thứ i *)
Trang 19
Uses Crt;
Const inf=25000;
Type arrn=array[1..50] of integer;
arrnl=array[1..50] of real;
Var c,a : arrnl;
x,xopt, ind : Arrn;
n : Integer;
w,fopt,cost,weight: Real;
Procedure Khoitao;
Var i,j,tg: integer;
t:real;
Begin
Fopt:=0; Weight:=0; {Tong gia tri, trong luong ban dau cua tui la 0 }
For i:=1 to n do ind[i]:=i; {Ghi nhan thu tu ban dau cua cac do vat }
(* Sap xep do vat theo thu tu khong tang cua c[i]/a[i] nham som tim thay
cau hinh tot nhat*)
For i:=1 to n1 do
Begin
For j:=i+1 to n do
if (c[i]/a[i]
Begin
t:=c[i]; c[i]:=c[j]; c[j]:=t;
t:=a[i]; a[i]:=a[j]; a[j]:=t;
tg:=ind[i]; ind[i]:=ind[j]; ind[j]:=tg;
End;
End;
End;
Trang 20
Procedure readfile;
Var j: integer;
f: text;
Name: String;
Begin
Write('Cho ten file du lieu:'); readln(name);
Assign(f,name);
Reset(f);
Readln(f,n,w);
For j:=1 to n do read(f,a[j]);
readln(f);
For j:=1 to n do read(f,c[j]);
Close(f);
Writeln('Trong luong cai tui: ',w:4:0);
Writeln('VEC TO GIA TRI DO VAT:');
For j:=1 to n do write(c[j]:4:0); writeln;
Writeln('VEC TO TRONG LUONG DO VAT:');
For j:=1 to n do write(a[j]:4:0); writeln;
End;
Procedure Ghinhan_ky_luc;
Var i:integer;
Begin
{Neu tong gia tri cua cac do vat tai cau hinh dang xet > gia tri ky luc truoc
do thi cap nhat lai cau hinh tot hon}
if Cost>fopt then
Begin
xopt:=x; {Ghi nhan cau hinh do vat moi}
Trang 21
Fopt:=cost;{Ghi nhan gia tri moi cua tui}
End;
End;
Procedure Nhanh_can(i:integer);
Var j,t:integer;
Begin
t:=trunc((wweight)/a[i]); {Trong luong con lai cua tui co the chua bao
nhieu do vat thu i}
For j:=t downto 0 do
Begin
x[i]:=j;
weight:=weight+a[i]*x[i];
Cost:=cost+c[i]*x[i];
if i=n then Ghinhan_ky_luc
Else
if cost+c[i+1]*(wweight)/a[i+1]> fopt then Nhanh_can(i+1);
weight:=weighta[i]*x[i];
Cost:=costc[i]*x[i];
End;
End;
Procedure Inkq;
Var i,j:integer;
Begin
Writeln('********** KET QUA TINH TOAN ***********');
Writeln('Tong gia tri cua cac do vat: ', fopt:4:0);
Writeln('Phuong an lay do:');
For j:=1 to n do writeln('So luong do vat ',ind[j],' la: ',xopt[j]:4,' lan');
Trang 22
Writeln('******************************************');
Write('Nhan enter de tiep tuc'); readln;
End;
BEGIN
Clrscr;
Readfile;
Khoitao;
Nhanh_can(1);
Inkq;
Readln;
END.
Giả sử ta có File dữ liệu là Caitui.txt trong ổ G có nội dung:
4 20
3 5 4 6
20 10 8 15
Thì sau khi chay xong chương trình ta có kết quả:
Cho ten file du lieu: G:\caitui.txt
Trong luong cai tui:
20
VEC TO GIA TRI DO VAT:
20 10 8 15
3 5 4 6
********KET QUA TINH TOAN*********
Tong gia tri cua cac do vat:
120
Phuong an lay do:
So luong do vat 1 la: 6 lan.
So luong do vat 4 la: 0 lan.
So luong do vat 3 la: 0 lan.
Trang 23
So luong do vat 2 la: 0 lan.
********************************************
Bài 2: Bài toán người du lịch
Một người du lịch muốn đi tham quan n thành phố T1, T2,..., Tn.
Xuất phát từ một thành phố nào đó người du lịch muốn đi qua tất cả các
thành phố còn lại, mỗi thành phố đúng một lần, rồi quay lại thành phố
xuất phát. Cij là chi phí đi từ thành phố Ti đến thành phố Tj (i,j=1,2,...,n),
hãy tìm một hành trình (một cách đi thỏa mãn điều kiện đặt ra) với tổng
chi phí nhỏ nhất.
Cố định thành phố xuất phát là T1, bài toán người du lịch dẫn về bài
toán:
f ( x2 , x3 ,..., xn ) = c[1, x2 ] + c[x2 , x3 ] + ... + c[xn −1 , xn ] + c[xn , x1 ]
Min
Với điều kiện (x2,x3,...,xn) là hoán vị của các số 2,3,...,n.
Ký hiệu cmin = min{c[i, j ]; i, j = 1, 2,..., n; i
j} là chí phí nhỏ nhất đi lại giữa
các thành phố.
Giả sử ta đang có phương án bộ phận (u1,u2,...,uk). Phương án này tương
ứng với thành phần bộ phận đi qua k thành phố:
T1
T (u2 )
...
T (uk −1 )
T (u k )
Vì vậy chi phí phải trả cho hành trình bộ phận này là:
σ = c[1, u2 ] + c[u2 , u3 ] + ... + c[uk −1 , uk ]
Để phát triển hành trình bộ phận này thành hành trình đầy đủ, ta phải đi
qua nk thành phố còn lại rồi quay về thành phố T1, tức là còn đi qua n
k+1 đoạn đường nữa. Do chi phí phải trả cho việc đi qua mỗi một trong
số nk+1 đoạn đường còn lại đều không nhỏ hơn cmin nên cận dưới cho
phương án bộ phận (u1,u2,...,uk) có thể được tính theo công thức:
g ( u1 , u 2 ,..., u k ) = σ + (n − k + 1).cmin .
Trang 24
Dưới đây là chương trình pascal giải bài toán người du lịch theo thuật
toán quay lui có đánh giá cận:
Uses Crt;
Var c :array[1..20,1..20] of integer;
a,xopt
:array[1..20] of integer;
chuaxet
:array[1..20] of boolean;
n,fopt,cmin,can
:integer;
Procedure readfile;
Var f
:Text;
Name :String;
i,j
:integer;
Begin
Write('Nhap ten file du lieu:');
Readln(Name);
Assign(f,name);
Reset(f);
Readln(f,n);
For i:=1 to n do
Begin
For j:=1 to n do read(f,c[i,j]);
readln(f);
End;
Close(f);
Cmin:=Maxint;{Maxint=32767}
For i:=1 to n do
For j:=1 to n do
if(i<>j) and (cmin>c[i,j]) then cmin:=c[i,j];
Trang 25