1. Trang chủ
  2. » Công Nghệ Thông Tin

Thuật toán lùa bò vào chuồng

5 10,1K 117
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 5
Dung lượng 39 KB

Nội dung

Thuật toán lùa bò vào chuồng

Trang 1

Thuật toán Lùa bò vào chuồng

Văn Sơn

Trong kỹ thuật lập trình, các bài toán có liên quan đến đếm các đối tượng rất gần gũi và dễ giải đối với chúng ta vì tính dễ xây dựng thuật toán cho nó Tuy nhiên, những bài toán

″đếm″ thường yêu cầu với dữ liệu rất lớn Nếu không biết tổ chức dữ liệu và thuật toán hiệu quả thì khó có thể đưa ra lời giải Một trong những cách tiếp cận để xử lý hiện tượng này là sử dụng thuật toán ″lùa bò vào chuồng″

Tư tưởng của thuật toán được xây dựng dựa trên suy nghĩ thực tế là để đếm số lượng bò trên một vùng xác định thì người ta phải tìm cách lùa chúng vào các chuồng (để chúng khỏi chạy rông) cho dễ đếm Đương nhiên là những con bò cùng loại phải được lùa vào cùng một chuồng để dễ phân biệt hoặc lùa mỗi con vào một chuồng thì càng tốt Tương tự như vậy, muốn giải bài toán đếm các đối tượng thì ta dùng một cấu trúc dữ liệu hợp lý (thường là mảng tĩnh hay mảng động) với ý nghĩa giống như các ″chuồng″ để lưu các đối tượng, mỗi phần tử của mảng tương ứng với một chuồng Trên cơ sở đó ta dễ dàng thực hiện được mục đích của mình là thực hiện thao tác đếm

Để hiểu thuật toán ta xét ví dụ sau: ″Hãy đếm và cho biết số lần xuất hiện của các chữ cái

La tinh trong một file văn bản Giả sử văn bản được viết toàn chữ hoa″ Rõ ràng với bài toán này ta có thể duyệt toàn bộ file và mỗi lần duyệt ta thực hiện thao tác đếm một chữ cái Sau 26 lần duyệt tương ứng với 26 chữ cái thì ta thu được toàn bộ kết quả Tuy nhiên,

vì việc duyệt file tương đối chậm và có thể file văn bản đã cho rất dài nên về thời gian là không chấp nhận được Ta vận dụng thuật toán trên bằng cách sử dụng một mảng tĩnh A:array[′A′ ′Z′] of Longint với ý nghĩa như sau: giá trị A[c] trong đó c thuộc [′A′ ′Z′] lưu

số lần xuất hiện của ký tự c trong file văn bản Mảng A được khởi tạo với tất cả các giá trị

bằng 0, khi duyệt file ta đọc lần lượt các ký tự trong file ra một biến trung gian ch nào đó,

và tăng giá trị của mảng tại vị trí tương ứng có chỉ số ch lên một đơn vị qua câu lệnh:

A[ch]:=A[ch]+1 Như vậy, bài toán được giải quyết chỉ với một lần duyệt file duy nhất Sau đây chúng ta cùng khảo sát một số ví dụ điển hình vận dụng thuật toán này

Bài 1 Tần số (Đề thi Olimpic sinh viên 2001, khối đồng đội)

Cho một văn bản gồm không quá N (N ≤500) dòng, mỗi dòng chứa không quá 80 ký tự Ta gọi tần số của một ký tự trong văn bản là số lần xuất hiện của ký tự đó trong văn bản

Yêu cầu: Tìm tần số lớn nhất trong các tần số của các chữ cái (không phân biệt chữ hoa

hay chữ thường) trong văn bản đã cho

Dữ liệu vào: từ file văn bản có tên là FREQ.INP:

Dòng đầu tiên chứa N là số lượng dòng văn bản

N dòng tiếp theo mỗi dòng chứa một dòng của văn bản đã cho

Kết quả: ghi ra file văn bản có tên FREQ.OUT tần số lớn nhất tìm được.

Ví dụ:

FREQ.INP

4

faculty of technology

Hanoi National University

Trang 2

cccCCCeeefffggg123456$#)(*+=

FREQ.OUT

25

Lời giải cho bài toán này gần giống như ví dụ đã trình bày ở trên Bạn đọc có thể xem thuật giải thông qua chương trình mẫu dưới đây:

var a:array[1 256] of longint;

n:integer;

procedure solve;

var f:text;i,j,k:integer;

s:string; ss:set of byte;

begin fillchar(a,sizeof(a),0);

ss:=[65 90];

assign(f,′FREQ.INP′); reset(f);

readln(f,n);

for i:=1 to n do

begin readln(f,s);

for j:=1 to length(s) do

begin

k:=ord(s[j]);

if k in ss then k:=k+32;

inc(a[k]);

end;

end;

close(f);

end;

procedure output;

var f:text; i,m:longint;

begin

assign(f,′FREQ.OUT′);

Rewrite(f);

m:=a[1];

for i:=2 to 256 do

if m < a[i] then m:=a[i];

write(f,m); close(f);

end;

begin

solve;

output;

end

Bài 2 Phủ nhỏ nhất (Đề thi chọn học sinh giỏi Quốc gia lớp 12 năm 1998-1999)

Cho tập hợp n đoạn thẳng (đánh số từ 1 đến n) với các đầu mút có toạ độ nguyên [Li,Ri], i=1,2, 3,…,n và một đoạn thẳng [P,Q] (P, Q là các số nguyên)

Yêu cầu: Cần tìm một số ít nhất đoạn thẳng trong tập đã cho để phủ kín đoạn thẳng [P,Q]

(nghĩa là mỗi điểm x thuộc [P,Q] phải thuộc vào ít nhất một trong số các đoạn thẳng được chọn)

Trang 3

Dữ liệu vào: từ file văn bản PHU.INP:

- Dòng đầu tiên ghi 3 số n, P, Q phân cách nhau bởi dấu trắng;

- Dòng thứ i trong số n dòng tiếp theo chứa hai số L, R phân cách nhau bởi dấu trắng (i=1, 2,…, n); 1 ≤ n ≤ 100 000; P − Q ≤5000; |Li| ≤ 50000; |Ri|≤50000,

i = 1, 2,…, n

Kết quả: ghi ra file văn bản PHU.OUT:

- Dòng đầu tiên ghi số k là số lượng đoạn cần chọn (quy ước ghi số 0 nếu không tìm được lời giải);

- Nếu k > 0 thì mỗi dòng trong số k dòng tiếp theo ghi số của một đoạn thẳng được chọn

Ví dụ:

Thuật toán: Ta phân tích bài toán để có thể áp dụng sáng tạo tư tưởng của thuật toán đã

trình bày như sau: Ta thấy rằng độ dài đoạn thẳng [P,Q] không lớn hơn 5000 Còn số đoạn thẳng trong tập đã cho có thể lên tới 100000 Ta không thể lưu tất cả các đoạn thẳng này lại được Vậy chúng ta có thể lưu các đoạn thẳng này theo cách: lưu từng đoạn một, dùng một mảng A có độ dài chính bằng độ dài đoạn thẳng [P,Q], kích thước của mảng này là 5000x2 (A: Array [ 0 5000, 1 2 ] Of LongInt;)

Ta coi đoạn [P,Q] là mảng A trên Ta lần lượt đọc các đoạn thẳng của tập n các đoạn thẳng Với mỗi đoạn thẳng vừa đọc được, ta xét xem chúng phủ đoạn [P,Q] bắt đầu từ đâu

và độ dài mà đoạn thẳng đó phủ được Ta sẽ lưu lại trên mảng A ở phần tử mà đoạn thẳng chúng ta vừa đọc bắt đầu phủ đoạn [P,Q] chỉ số của đoạn thẳng đó và độ dài chúng phủ được

A[Phần tử bắt đầu phủ,1] = chỉ số của đoạn vừa đọc và A[Phần tử bắt đầu phủ, 2] =

độ dài mà đoạn đó phủ được.

Nếu có nhiều đoạn thẳng bắt đầu phủ đoạn [P,Q] tại cùng một vị trí thì chọn đoạn nào có

độ dài phủ được là lớn nhất

Sau khi làm xong công việc trên ta được một mảng gồm các đoạn thẳng phủ lên đoạn [P,Q] Bây giờ ta sẽ tiến hành tìm xem số đoạn phủ ít nhất sẽ là bao nhiêu bằng cách: ta bắt đầu đi từ vị trí 0 (nếu tại vị trí này mà không có đoạn nào phủ thì sẽ không có lời giải) ta tìm xem có đoạn nào giao với đoạn này mà tạo thành một đoạn thẳng có phủ dài nhất thì ta lưu đoạn này lại (nếu không có đoạn thẳng nào giao với đoạn thẳng này để tạo được một đoạn thẳng có độ che phủ lớn hơn độ che phủ của đoạn trên thì sẽ không có lời giải) Làm tương tự với đoạn thẳng vừa tìm được cho đến khi các đoạn thẳng được chọn sẽ phủ kín đoạn [P,Q] ta sẽ được số đoạn thẳng ít nhất để phủ đoạn [P,Q] Như vậy, thuật toán này cho phép giải quyết bài toán cũng chỉ với một lần duyệt mảng duy nhất

Dưới đây là chương trình minh hoạ:

Const Fi = ′PHU.INP′; Fo = ′PHU.OUT′;

Max = 5000; Dd = 2;

Type Vec1 = Array [ 0 Max,1 2 ] Of LongInt;

Vec2=Array[0 Max] Of LongInt;

Var n, P, Q, Min,Id : LongInt;

A : Vec1; Chon : Vec2;

Trang 4

Procedure InitData;

Var i, L, R : LongInt; F : Text;

Begin

Id:=1;

Assign(F, Fi); Reset(F);

Readln(F,n,P,Q);

FillChar(A, SizeOf(A),0);

For i := 1 To n Do

Begin

Readln(F,L,R);

If L <= P Then

If A[0, Dd] < R - P Then

Begin A[0, id] := i;

If R > Q Then A[0, Dd] := Q - P

Else A[0, Dd] := R - P;

End Else

Else If L <= Q Then

If A[L - P, Dd] < R - L Then

Begin

A[L - P, id] := i;

If R > Q Then

A[L - P, Dd] := Q - L

Else A[L - P, Dd] := R - L;

End; End; Close(F);

End;

Function tt(u,v:LongInt):LongInt;

Begin

If A[u, Dd]+u<=A[v,Dd]+v Then tt:=A[v,Dd]+v Else tt:=A[u,Dd]+u;

End;

Procedure solve;

Var i,j,tmp,Count,Max:LongInt;

Dung:Boolean;

Begin

Min:=0;

If A[0,id]=0 Then Exit;

j:=0;

Dung:=False;

Count:=1;

Chon[1]:=A[0,id];

If A[0, Dd] = Q-P Then

Begin Min:=1; Exit; End;

While Not Dung Do

Begin

Max:=j+A[j,Dd];

For i:=j + 1 To j + A[j,Dd] Do

Trang 5

If (A[i, id]<>0) And (tt(i, j)>Max) Then

Begin

Max:=tt(i,j); tmp:=i;

End;

If Max=j+A[j,Dd] Then Exit

Else Begin

Count:=Count + 1;

Chon[Count]:=A[tmp, id];

j :=tmp;

If Max = Q - P Then

Dung := True;

End; End;

Min := Count;

End;

Procedure ReSult;

Var F : Text; i : LongInt;

Begin

Assign(F, FO);

ReWrite(F);

Writeln(F, Min);

If Min <> 0 Then

For i: = 1 To Min Do Writeln(F, Chon[i]); Close(F);

End;

Begin

InitData;

solve;

ReSult;

End

Bài tập Các số lặp

Cho một dãy số nguyên gồm N phần tử Lập chương trình in ra số nguyên lặp nhiều nhất trong dãy (N<106) (Gợi ý: Chúng ta nên sử dụng mảng vớ cấu trúc dữ liệu kiểu con trỏ)

Ngày đăng: 11/09/2012, 15:00

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w