Các thuật tốn duyệt: Duyệt tồn bộ: Một trong những phương pháp hiển nhiên nhất để giải bài tốn tối ưu tổ hợp đặt ra là: trên cơ sở các thuật tốn liệt kê tổ hợp ta tiến hành duyệt từng p
Trang 1BÀI TOÁN TỐI ƯU :
Bài tốn tối ưu tổ hợp là bài tốn tìm phương án tối ưu trên tập các cấu hình tổ hợp Nghiệm của bài tốn cũng là một vector x gồm n thành phần sao cho:
1 x = (x1,x2,…xn)
2 xi lấy giá trị trong tập Si
3 x thoả mãn các ràng buộc cho bởi hàm G(x)
4 F(x) min/max (hàm F(x) được gọi là hàm mục tiêu)
Khi đĩ x gọi là một phương án tối ưu, F(x) là giá trị tối ưu.
1 Các thuật tốn duyệt:
Duyệt tồn bộ:
Một trong những phương pháp hiển nhiên nhất để giải bài tốn tối ưu tổ hợp đặt ra là: trên cơ sở các thuật tốn liệt kê tổ hợp ta tiến hành duyệt từng phương án của bài tốn, đối với mỗi phương án ta điều tính giá trị hàm mục tiêu tại đĩ, sau đĩ so sánh giá trị hàm mục tiêu tại tất cả các phương án được liệt kê để tìm ra phương án tối ưu
Thuật tốn nhánh cận:
Trang 2Trong thủ tục trên, BestSolution là nghiệm tốt nhất đã biết ở thời điểm đĩ Thủ tục <cập nhật BestSolution> sẽ xác định “độ tốt” của nghiệm mới tìm thấy, nếu nghiệm mới tìm thấy tốt hơn BestSolution thì BestSolution sẽ được cập nhật lại là nghiệm mới tìm được
2 Một số ví dụ
BÀI TỐN NGƯỜI DU LỊCH
1 Bài tốn
Cho n thành phố đánh số từ 1 đến N và các tuyến đường giao thơng hai chiều giữa chúng, mạng lưới giao thơng này được cho bởi bảng C[1 n,1 n], ở đây Cij = Cji là chi phí đi đoạn đường trực tiếp từ thành phố i đến thành phố j Một người 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 1 lần và cuối cùng quay lại thành phố 1 Hãy chỉ ra cho người đĩ hành trình với chi phí ít nhất Bài tốn đĩ gọi là bài tốn người du lịch hay bài tốn hành trình của một thương
gia (Travelling Salesman Problem-TSP)
* Input: file TOURISM.INP cĩ dạng:
- Dịng đầu chứa số thành phố n (1<n100)
- n dịng tiếp theo, mỗi dịng n số mơ tả mảng C (chi phí này là số nguyên dương ≤10000)
* Output: file TOURISM.OUT cĩ dạng:
- Dịng đầu là chi phí ít nhất
- Dịng thứ hai mơ tả hành trình
Procedure BranchBound(i); // xây dựng thành phần thứ i
Begin
<Đánh giá các nghiệm mở rộng>;
If<các nghiệm mở rộng đều không tốt hơn BestSolution> then exit;
<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 “i=n”) then < Cập nhật BestSolution>
else BranchBound(i+1);
<Loại thành phần i >;
end;
end;
Trang 3Ví dụ:
4
0 20 35 42
20 0 34 30
35 34 0 12
42 30 12 0
97 1->2->4->3->1
2 Cách giải
1) Hành trình cần tìm cĩ dạng (x1 = 1, x2, , xn, xn+1 = 1), ở đây giữa xi và xi+1: hai thành phố liên tiếp trong hành trình phải cĩ đường đi trực tiếp; trừ thành phố 1, khơng thành phố nào được lặp lại hai lần
Cĩ nghĩa là dãy (x1, x2, , xn) lập thành 1 hốn vị của (1, 2, , n)
2) Duyệt quay lui: x2 cĩ thể chọn một trong các thành phố mà x1 cĩ đường đi tới (trực tiếp), với mỗi cách thử chọn x2 như vậy thì x3 cĩ thể chọn một trong các thành phố mà x2 cĩ đường đi tới (ngồi x1) Tổng quát: xi cĩ thể chọn 1 trong các thành phố chưa đi qua mà từ x i-1 cĩ đường đi trực tiếp tới (1 i n)
3) Nhánh cận: Khởi tạo cấu hình BestConfig cĩ chi phí = + Với mỗi bước thử chọn xi xem chi phí đường đi cho tới lúc đĩ cĩ < Chi phí của cấu hình BestConfig?, nếu khơng nhỏ hơn thì thử giá trị khác ngay bởi cĩ đi tiếp cũng chỉ tốn thêm Khi thử được một giá trị xn ta kiểm tra xem xn cĩ đường đi trực tiếp về 1 khơng ? Nếu cĩ đánh giá chi phí đi từ thành phố 1 đến thành phố xn cộng với chi phí từ xn đi trực tiếp về 1, nếu nhỏ hơn chi phí của đường đi BestConfig thì cập nhật lại BestConfig bằng cách đi mới
Chương trình giải bài tóan người du lịch bằng PP nhánh cận (tham khảo):
Trang 4Program Nguoi_dulich;
{TT: ky thuat nhanh can}
Const MAX =100; oo =1000000;
fi ='Tourism.INP'; fo ='Tourism.OUT';
Var
C :array[1 max,1 max]of integer;
x,bestway:array[1 max]of integer;
d : array[1 max] of boolean;
n,m :integer;
sum,best : longint;
Procedure input;
var
i, j, k: Integer; f: Text;
begin
Assign(f,fi); Reset(f);
ReadLn(f, n);
for i :=1 to n do
for j := 1 to n do
Read(f,C[i, j]);
Close(f);
end;
procedure update;
begin
if sum+C[x[n],x[1]]<best then
begin
best:=sum+C[x[n],x[1]];
bestway:=x;
end;
end;
procedure branchBound(i:longint);
var j :longint;
begin
if sum>=best then exit;
for j:=1 to n do
if d[j] then
begin
x[i]:=j;
d[j]:=false;
sum:=sum + C[x[i-1],j];
if i=n then update
else branchBound(i+1);
sum:=sum - C[x[i-1],j];
d[j]:=true;
end;
end;
procedure init;
begin
fillchar(d,sizeof(d),true);
d[1]:=false;
x[1]:=1;
best:=oo;
Trang 5procedure output;
var f:text; i:longint;
begin
assign(f,fo); rewrite(f);
writeln(f,best);
for i:=1 to n do
write(f,bestway[i],'->');
write(f,bestway[1]);
close(f);
end;
BEGIN
input; init;
BranchBound(2);output;
END
Trang 6BÀI TẬP PHƯƠNG PHÁP DUYỆT TỒN BỘ, NHÁNH CẬN
Bài 1 BÀI TỐN MÁY RÚT TIỀN TỰ ĐỘNG ATM:
Chương trình giải bài tóan ATM (tham khảo):
Program ATM_PP_NhanhCan;
Const MAX =20; fi ='ATM.INP';
fo ='ATM.OUT';
Type vector =array[1 MAX] of longint;
Var t, tmax :array[1 MAX] of longint;
x,xbest :vector;
c,cbest :longint;
n,s,sum :longint;
Procedure input;
var f :text;
i :longint;
begin
assign(f,fi); reset(f);
readln(f,n, s);
for i:=1 to n do read(f,t[i]);
close(f);
end;
Trang 7Procedure init;
var i :longint;
begin
tmax[n]:=t[n];
for i:=n-1 downto 1 do begin
tmax[i]:=tmax[i+1];
if tmax[i]<t[i] then tmax[i]:=t[i];
end;
sum:=0; c:=0; cbest:=n+1;
end;
Procedure update;
var i :longint;
f :text;
begin
if (sum = s) and (c<cbest) then begin
xbest:=x; cbest:=c;
end;
end;
Procedure OUTPUT;
var i :longint;
f :text;
begin
assign(f,fo); rewrite(f);
if cbest<n+1 then begin
writeln(f,cbest);
for i:=1 to n do
if xbest[i]=1 then write(f,t[i],' ');
end
else write(f,'-1');
close(f);
end;
Procedure BranchBound(i:longint);
var j :longint;
begin
if c + (s-sum)/tmax[i] >= cbest then exit;
for j:=0 to 1 do begin
x[i]:=j;
sum:=sum + x[i]*t[i];
c:=c + j;
if (i=n) then update
else if sum<=s then branchBound(i+1);
sum:=sum - x[i]*t[i];
c:=c - j;
end; end;
BEGIN
Input;
Init;
BranchBound(1);
Output;
END
Trang 8Bài 2: Trong một Hội chợ triển lãm có các gian hàng được bố trí trong một khung gồm NxN ô, mỗi ô
một gian hàng chứa các mặt hàng khác nhau Từ một gian hàng muốn đi qua gian hàng khác ta có thể
đi theo 4 hướng: lên, xuống, trái, phải Một người xuất phát từ ô [1,1] muốn đi xem tất cả các gian hàng, mỗi gian hàng không quá 1 lần và không bỏ gian hàng nào, sau đó ra cửa ở ô [n,1]
Yêu cầu tìm các đường đi mà người đó có thể thực hiện được
Input : số nguyên n (n<=10) Output: gồm nhiều dòng, mỗi dòng là 1 đường đi
Bài 3: Mã đi tuần: Cho bàn cờ có N x N ô Một quân Mã được phép đi theo luật cờ Vua, đầu tiên
được đặt ở ô có tọa độ (x,y) Lập chương trình tìm tất cả các đường đi của quân Mã sao cho mọi ô trên bàn cờ đều được quân Mã đi qua đúng 1 lần
Bài 4: Truyền tin
Người ta cần truyền n gói tin được đánh số từ 1 đến n từ một điểm phát đến một điểm thu Để thực hiện việc truyền tin có thể sử dụng m đường truyền được đánh số từ 1 đến m Biết rằng nếu truyền j gói tin theo đường truyền tin I thì chi phí phải trả là Sij (Sij là số nguyên dương, Sij≤32767, i=1,2,…,m, j=1,2,…,n)
Yêu cầu: Hãy xác định số lượng gói tin cần truyền theo mỗi đường truyền tin để việc truyền n gói tin được thực hiện với tổng chi phí là nhỏ nhất
Dữ liệu: vào từ file TTIN.INP:
+Dòng đầu tiên chứa hai số nguyên dương n va m (n,m<100)
+Dòng thứ i trong số m dòng tiếp theo chứa n số nguyên dương Si1, Si2,…,Sin, i=1,2,…,m
Kết quả: Đưa ra file TTIN.OUT:
+Dòng đầu tiên chứa S là tổng chi phí phải trả theo cách tuyền tin tìm được
+ Dòng thứ hai chứa m số nguyên không âm q1,q2,…,qm, trong đó qi là số gói tin cần truyền theo đường truyền tin i
Ví dụ:
Hướng dẫn:
Mỗi đường truyền i có thể truyền j gói tin (j=0,1,2,…,n-t, t là tổng các gói tin đã truyền trên i-1 đường truyền trước)
Do đó, mỗi một phương án truềyn tin là một chỉnh hợp lặp chặp m của n-t+1 phần tử {0,1,2,…,n-t} có dạng q=(q1,q2,…,qm)
Giá của phương án là
Bài 5: Thuê máy
Một trung tâm có n máy tính (cấu hình giống nhau) dùng để cho thuê trong tháng (từ ngày 1 đến ngày 31), thời gian thuê tính theo đơn vị ngày Đầu tháng, trung tâm nhận được yêu cầu của m khách hàng (đánh số từ 1 đến m), mỗi khách hàng cần thuê một máy trong một số ngày nào đấy thuộc tháng Giả thiết rằng, đối với mỗi khách hàng, hoặc trung tâm từ chối, hoặc trung tâm đồng ý cho thuê theo
đúng như yêu cầu của khách Với mỗi ngày, gọi tần số sử dụng máy là số máy tính được sử dụng trong
ngày đó Hãy lập một phương án cho thuê máy để tổng tần số sử dụng máy trong tháng là lớn nhất
* Dữ liệu: vào từ file THUEMAY.INP:
+Dòng đầu ghi các giá trị n,m
3 3
20 20 20
4 3 10
1 3 20
4
0 2 1
1
( ) m [ , ]i
i
Trang 9+m dòng tiếp theo, theo thứ tự 1,2, ,m mỗi dòng ghi yêu cầu của một khách : bắt đầu là số ngày
mà khách cần thuê, tiếp theo là các ngày trong tháng mà khách cần thuê
* Kết quả: Đưa ra file TTIN.OUT:
+ Dòng đầu ghi số khách được thuê
+ Dòng tiếp theo ghi các số hiệu khách được thuê, các số hiệu này phân cách nhau ít nhất một dấu cách
+ Dòng cuối cùng ghi tổng tần số sử dụng máy
Hạn chế kích thước: số máy, số khách không quá 30
* Hướng dẫn:
- Mỗi phương án cho thuê máy tính là một dãy nhị phân độ
dài m, x=(x1,x2,…,xm) xi{0,1}
- Điều kiện chấp nhận x là
- Giá của phương án là :
Duyệt các dãy nhị phân độ dài m, kiểm tra các dãy là
phương án của bài toán, so sánh giá của các phương án với nhau để tìm phương án có giá lớn nhất
3 10
5 1 3 4 6 7
1 1
2 3 6
3 2 4 5
6 1 2 3 4 5 6
2 2 3
5 1 3 4 6 7
5 2 4 5 6 7
4 1 2 4 5
3 3 5 6
6
2 6 7 8 9 10 20
1 [ , ]* [ ] , 1, 31
m
i
ngay i j x i n j
1
( ) m [ ]* [ ]
i
Trang 10Program HoiCho;
Const
max = 100;
fi='HOICHO.INP';
fo='HOICHO.OUT';
Hx : Array[1 4] of integer=(0,0,-1,1);
Hy : Array[1 4] of integer=(-1,1,0,0);
Var
Dd : Array[1 Max,1 max] of integer;
n, x, y, sn :integer;
f :text;
{+ + + + + ++ + + + + + + + + + + + + + + +}
Procedure input;
begin
Assign(f,fi);Reset(f);
Readln(f,n);
Fillchar(Dd,sizeof(Dd),0);
x:=1; y:=1;
DD[x,y]:=1;
close(f);
end;
{+ + + + + ++ + + + + + + + + + + + + + + +}
Procedure ghinghiem;
var i,j : longint;
begin
if dd[n,1]=n*n then begin
sn:=sn+1;writeln(f,'Nghiem thu :',sn);
for i:=1 to n do begin
for j := 1 to n do write(f,dd[i,j]:5);
writeln(f);
end;
end;
end;
{+ + + + + ++ + + + + + + + + + + + + + + +}
Procedure Tim(t:integer);
var i, u, v : longint;
begin
for i := 1 to 4 do begin
u := x + Hx[i]; v := y + Hy[i];
if ((u>=1) and (u<=n)) and ((v>=1) and (v<=n)) then
if (dd[u,v]=0) then
begin
dd[u,v] := t; x := u; y := v;
if t=n*n then ghinghiem
else
Tim(t+1);
x := u - Hx[i]; y := v - Hy[i];
dd[u,v]:=0;
end;
end;
Trang 11end;
{+ + + + + ++ + + + + + + + + + + + + + + +}
BEGIN
input;
assign(f,fo);rewrite(f);
tim(2);
if sn=0 then writeln(f,'Bai toan vo nghiem')
else writeln(f,'Bai toan co ', sn,' nghiem');
close(f); END
HD Bài 3
Program Ma_di_tuan;
Const max = 20; fi='MADITUAN.INP';
fo='MADITUAN.OUT';
Hx :Array[1 8] of integer=(2,1,-1,-2,-2,-1,1,2);
Hy : Array[1 8] of integer=(1,2,2,1,-1,-2,-2,-1);
Var
DD : Array[1 Max,1 max] of integer;
n, x, y, sn :integer; s : Set of 1 Max;
f : text;
Procedure input;
var i:integer;
begin Assign(f,fi);Reset(f); Readln(f,n,x,y);
Fillchar(Dd,sizeof(Dd),0); DD[x,y]:=1;
s:=[]; for i:=1 to n do S:=S+[i];
close(f);
end;
{===============================}
Procedure ghinghiem;
var i,j : longint;
begin
sn:=sn+1;writeln(f,'Nghiem thu :',sn);
for i:=1 to n do begin
for j := 1 to n do write(f,dd[i,j]:5);
writeln(f); end;
end;
{===============================}
Procedure Tim(t:integer);
var i, u, v : longint;
begin
for i := 1 to 8 do begin
u := x + Hx[i]; v := y + Hy[i];
if (u In S) and (v In S) then
if (dd[u,v]=0) then
begin
dd[u,v] := t; x := u; y := v;
if t=n*n then ghinghiem
else
Trang 12Tim(t+1);
x := u - Hx[i]; y := v - Hy[i];
dd[u,v]:=0;
end;
end;
end;
{===============================}
BEGIN
input;
assign(f,fo);rewrite(f);
tim(2);
if sn=0 then writeln(f,'Bai toan vo nghiem')
else writeln(f,'Bai toan co ', sn,' nghiem');
close(f);
END
Giải Bài 4: DI DAO
Const fi='DIDAO2.INP';fo='DIDAO2.OUT';
Type Dao=Record
x,y:integer;
end;
mangso=Array[1 100] of integer;
Var a:array[1 100] of Dao;
b,c:mangso;
DD:array[1 100] of Boolean;
i,j, DDmin,dem,d, dau,dich,n,sn:integer;
nhoi, m:real; Good:Boolean;
f,g:text;
Procedure input;
Var k:integer;
begin
readln(f,n);
For k:=1 to n do Read(f,a[k].x,a[k].y);
Readln(f); Readln(f,dau,dich);
Read(f,m);
Fillchar(b,sizeof(b),0);
Fillchar(c,sizeof(c),0);
Fillchar(DD,sizeof(DD),False);
DDMin:=maxint;
end;
Procedure Print(x:mangso;sl:integer);
var k:integer;
begin
sn:=sn+1;
for k:=1 to sl-1 do write(g,x[k],' -> ');
writeln(g,x[sl]);
end;
Function kc(i,j:integer):real;
begin
kc:=sqrt(sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y));
Trang 13Procedure thu(i:integer);
var j,k:integer;
begin
if d=dich then
begin
print(b,dem);
if (Sn=1) Or (dem<DDMin) then
begin
ddmin:=dem;
fillchar(c,sizeof(c),0);
for k:=1 to dem do c[k]:=b[k];
end;
end;
For j:=1 to n do
Begin
if (j<>i) and (Not DD[j]) then
begin
Good:=true;
for k:=1 to dem do
if (a[j].x=a[k].x) and (a[j].y=a[k].y) then Good:=false;
if Good and (kc(i,j)<=m) then
begin
dem:=dem+1;
b[dem]:=j;
DD[j]:=true;
d:=j;
thu(d);
b[dem]:=0;
dem:=dem-1;
DD[j]:=False;
end; end; end; end;
Procedure xuli;
begin
sn:=0; dem:=1; b[1]:=dau;
thu(dau);
if sn=0 then
writeln(g,'Khong co DD tu ', dau ,' den ',dich)
else {if (DDmin>0) and (ddmin<maxint) then}
begin
writeln(g,'Tong so duong di : ',sn);
writeln(g,'Duong di toi uu : ');
print(c,DDmin);
end
end;
Begin
openf;
input; nhoi:=maxint;
xuli;
closef;
Trang 14Program TRUYENTIN;
const { t la tong goi tin da truyen tren i-1 duong truyen truoc}
Fi = 'TTIN.INP';
Fo = 'TTIN.OUT';
max = 100;
Type vector=array[1 max,0 max] of integer;
var
s: vector;
f: Text;
n,m: integer;
q,qc:array[1 max] of integer;
min, tongcp, sn:longint;
procedure Nhap;
var i,j:integer;
begin
Assign(f, Fi); Reset(f);
ReadLn(f, n,m);
for i:=1 to m do begin
for j:=1 to n do read(f,s[i,j]);
readln(f);
end;
Close(f);
for i:=1 to m do s[i,0]:=0;
end;
{ -}
Procedure chon_min;
var i:integer;
begin
if (tongcp<>0) and (sn=n) then
if tongcp<min then begin
min:=tongcp;
qc:=q;
end;
end;
{ -}
procedure Tim(i: Integer); { moi duong truyen i co the truyen j goi tin}
var j, t, k : Integer; { j=0,1,2, ,n-t, t la tong cac goi tin da truyen tren i-1 duong truyen truoc}
begin
t:=0;
for k:=1 to i-1 do
t:=t+q[k];
for j:=0 to n-t do begin