Tải bản đầy đủ (.pdf) (35 trang)

Sáng kiến kinh nghiệm Tin học lớp 10: Thuật toán quay lui và ứng dụng giải bài toán tối ưu

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 (517.18 KB, 35 trang )

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, ai­1, 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 ai­1.

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ừ ci­1+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[i­1]+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 i­1 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ả i­1 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[i­j],i­j={1­n,....,n­1}.

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[i­j] đề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:=1­n to n­1 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 [i­j] : = 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[i­1],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,…, xi­1) 
Đá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 n­1 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((w­weight)/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]*(w­weight)/a[i+1]> fopt then Nhanh_can(i+1);
          weight:=weight­a[i]*x[i];
          Cost:=cost­c[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 n­k 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ố n­k+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


×