• Nenhum resultado encontrado

Duyệt với độ ưu tiên.

No documento quyhoach_dong (páginas 152-162)

Bài 4: Xâu fibonacci.

1. Duyệt với độ ưu tiên.

Trước hết chúng ta nhớ lại phương pháp duyệt có cận: Cận là một điều kiện để kiểm tra đề cử có được chấp nhận hay không. Nhờ có cận

chúng ta có thể : loại bỏ một số đề cử không thoả mãn điều kiện, làm cho quá trình duyệt nhanh hơn. Việc thử mọi khả năng đề cử cho một phần tử của nghiệm cũng giống như tình trạng 'dò dẫm chọn đường' của một người đi đường, mỗi khi đến ngãN-đường phải lần lượt chọn từng con đường đi tiếp trong các con

đường xuất phát từ ngãN-đường đó. Nếu có được những điều 'chỉ dẫn' bảo đảm chắc chắn những con đường nào là đường 'cụt' không thể đi tới đích thì người đi đường sẽ loại ngay những con đường đó.

ta lại chú ý đến tình huống ngược lại: tìm những 'chỉ dẫn' cho biết chỉ : cần đi theo một số con đường nhất định trong N đường, coi những chỉ dẫn ấy như 'la bàn' chỉ phương hướng tìm kiếm đích của mình. Tất nhiên việc đưa ra những lời chỉ dẫn, những dự đoán và khẳng định điều này là 'đúng', điều kia là 'saí là việc thận trọng.

Những khẳng định tưởng là ' chắc chắn' nếu thực sự chỉ là điều 'ngộ nhận' thì có thể bỏ sót một số con đường tới đích, hoặc chệch hướng không thể tới đích!

Nhưng nói chung, những chỉ dẫn hợp lí sẽ đi tới đích hoặc gần với đích nhanh nhất. Điều này rất thích hợp với những bài toán thực tế chỉ yêu cầu tìm lời giải 'gần sát với nghiệm'. Khi đó người ta thường duyệt với độ ưu tiên. Nội dung duyệt với độ ưu tiên xuất phát từ ý tưởng heuristic (tìm kiếm): tìm một : 'chỉ dẫn' sao cho xác suất tới đích có thể chấp nhận. Công việc cụ thể là: + Sắp xếp các đề cử theo một 'khoá' (key) nào đó,

+ Sau khi sắp xếp, chỉ chọn một số đề cử ở các vị trí đầu (hoặc cuối) của danh sách đã sắp. Những đề cử này theo suy luận hợp lí về tính chất và giá trị của khoá sẽ bảo đảm là một : 'chỉ

dẫn' cho xác suất tới đích có thể chấp nhận. Nhược điểm của phương pháp này là có thể bỏ sót nghiệm hoặc chỉ tìm được lời giải 'gần sát với nghiệm'.

Để khắc phục, chúng ta có thể áp dụng nó nhiều lần, mỗi lần mở rộng thêm số lượng đề cử trong danh sách đề cử đãsắp. Đôi khi cũng phải thay đổi hoàn toàn, làm lại từ đầu: điều chỉnh và mở rộng thêm điều kiện chọn làm khoá sắp xếp cho hợp lí hơn.

Ví dụ: (Bài toán lập lịch cho sinh viên chọn môn học)

Sinh viên theo học các trường đại học thường rất bối rối bởi các quy tắc phức tạp đánh giá hoàn thành chương trình học. Việc hoàn thành

chương trình học đòi hỏi sinh viên có một số kiến thức trong một số lĩnh vực nhất định. Mỗi một đòi hỏi có thể được đáp ứng bởi nhiều môn học. Một môn học cũng có thể đáp ứng đồng thời nhiều đòi hỏi khác nhau. Các quy tắc này thường được phổ biến cho các sinh viên ngay khi họ mới vào trường. Do thời gian còn lại quá ít nên mỗi sinh viên đều muốn theo học ít môn học nhất mà vẫn đáp ứng được tất cả các đòi hỏi của chương trình học. Có M môn học được đánh

số từ 1 đến M. Có N đòi hỏi được đánh số từ 1 đến N. Với mỗi môn học cho biết danh sách các đòi hỏi được thoả mãn khi học môn này.

Cần viết một chương trình tìm ra một số ít nhất các môn học mà một sinh viên cần học để có thể hoàn thành chương trình học (nghĩa là đáp ứng được N đòi hỏi).

Dữ liệu vào từ file văn bản MONHOC.INP có cấu trúc:

ã Dòng đầu là 2 số nguyên dương M và N (M, N ≤ 200);

ã Trong M dòng sau, dòng thứ i là N số nguyên phân cách nhau bởi dấu cách mà số thứ j là 1 nếu môn học i đáp ứng được đòi hỏi j, là 0 trong trường hợp ngược lại.

Kết quả ghi ra file văn bản MONHOC.OUT có cấu trúc:

ã Dòng đầu tiên ghi số lượng ít nhất các môn học;

ã Dòng tiếp theo ghi số hiệu các môn học đó. Ví dụ:

Cách giải:

Duyệt chọn các môn học với độ ưu tiên phù hợp: mỗi lần duyệt các môn ta chỉ đề cử một số môn học chưa chọn có nhiều yêu cầu chưa thỏa mãn sẽ được thoả mãn. Số lượng các môn học được đề cử càng nhiều thì thời gian thực hiện chương trình càng lâu và có thể dẫn tới tràn stack, do đó khi lập trình cần điều chỉnh số lượng này cho phù hợp thời gian cho phép trong đề bài. Ví dụ mỗi lần ta sắp giảm các môn học còn lại theo số lượng yêu cầu còn cần phải đáp ứng mà mỗi môn có thể đáp ứng được, sau đó chỉ chọn ra một vài môn học đầu dãy đã sắp để làm đề cử khi duyệt. Ngoài ra với M, N 200 cũng cần đặt ngắt thời gian trong khi duyệt để thoát khỏi duyệt trong phạm vi thời gian còn cho phép.

Chương trình:

const fi = 'monhoc.inp'; fo = 'monhoc.out';

max = 200;

lim = 3; {số lượng môn học được đề cử tối đa là lim, lim có thể điều chỉnh cho phù hợp}

type km1 = array[1..max,1..max] of byte; km2 = array[1..max] of byte;

a : km1; {a[i,j]=1 : môn i đáp ứng được yêu cầu j} m,n : byte; kq,lkq, {kq[i] là môn được chọn thứ i trong phương án} dx_mon, {dx_mon[i]=1: môn i đãđược chọn} dx_yeucau: km2; {dx_yeucau[j]=k: yêu cầu j đãđược đáp ứng bởi môn được chọn thứ k} so_mon_chon, {số môn đãđược chọn}

lso_mon_chon : byte;

so_yc_dx : byte; {số yêu cầu đãđược đáp ứng} f : text;

procedure read_in; begin

{Đọc file input lấy giá trị cho các biến M, N và mảng A[1..M, 1..N]} end; procedure toi_uu; begin lkq:=kq; lso_mon_chon:= so_mon_chon; end;

function bac(i: byte): byte; begin

{Hàm cho biết số yêu cầu chưa được đáp ứng sẽ được đáp ứng nếu chọn môn i}

end;

procedure tao_decu(var sl: byte; var danhsach: km2);

var i,j,k : byte; b : km2;

begin

{Dùng mảng danhsach để chứa các môn chưa chọn, sau đó sắp giảm mảng này theo số lượng yêu cầu chưa được đáp ứng mà mỗi môn có thể đáp ứng. Cuối cùng chỉ giữ lại không quá lim môn (3 môn)} sl:=0; for i:=1 to m do if dx_mon[i]=0 then begin inc(sl); b[sl]:=bac(i); danhsach[sl]:=i; if b[sl]=0 then dec(sl); end;

if sl>1 then begin for i:=1 to sl-1 do for j:=i+1 to sl do if b[i] begin k:=b[i]; b[i]:=b[j]; b[j]:=k; k:=danhsach[i]; danhsach[i]:=danhsach[j]; danhsach[j]:=k; end; end;

if sl>lim then sl:=lim; end;

procedure nap(i: byte); {chọn môn i, xác nhận những yêu cầu do môn i đáp ứng} var j : byte; begin inc(so_mon_chon); kq[so_mon_chon]:=i; dx_mon[i]:=1; for j:=1 to n do

if (a[i,j]=1) and (dx_yeucau[j]=0) then begin

dx_yeucau[j] := so_mon_chon; inc(so_yc_dx);

end; end;

procedure bo(i: byte); {Không chọn môn i, xác nhận lại những yêu cầu chưa được đáp ứng} var j : byte; begin for j:=1 to n do if dx_yeucau[j]=so_mon_chon then dx_yeucau[j]:=0; dec(so_mon_chon); dx_mon[i]:=0; end;

procedure try; {duyệt với các đề cử được ưu tiên}

var i,j,sl,lso_yc_dx : byte; danhsach : km2;

begin

if (meml[$0:$046c]-time)/18.2>30 then exit; if so_mon_chon >= lso_mon_chon then exit; if so_yc_dx=n then

tao_decu(sl,danhsach);

lso_yc_dx:=so_yc_dx; {lưu lại số yêu cầu đãđáp ứng trước khi chọn đề cử, để phục vụ bước quay lui}

for i:=1 to sl do {chỉ duyệt với số lượng đề cử là sl}

begin

nap(danhsach[i]); {xác nhận đề cử thứ i trong danh sách ưu tiên}

try;

bo(danhsach[i]); {Quay lui: xoá xác nhận đề cử} so_yc_dx:=lso_yc_dx; {Lấy lại số yêu cầu

đãđáp ứng trước khi chọn đề cử } end;

end;

procedure hien_kq; begin

{Ghi vào file output số môn chọn ít nhất là

lso_mon_chon và số hiệu các môn được chọn là giá trị các phần tử của mảng lkq}

BEGIN clrscr; read_in; lso_mon_chon:=m+1; {Khởi trị số môn được chọn ít nhất là m+1} time:= meml[$0:$046C]; try; hien_kq; END.

No documento quyhoach_dong (páginas 152-162)

Documentos relacionados