Đánh dấu phần tử đợc chọn1- ý tởngchung Kỹ thuật đánh dấu phần tử là một trong những kỹ thuật nhằm giúp cho ngời lập trình tạo đợc những thuật toán đơn giản để giải quyết vấn đề đặt ra..
Trang 1Đánh dấu phần tử đợc chọn
1- ý tởngchung
Kỹ thuật đánh dấu phần tử là một trong những kỹ thuật nhằm giúp cho ngời lập trình tạo đợc những thuật toán đơn giản để giải quyết vấn đề đặt ra.
Để đánh dấu phần tử đợc chọn, ta khai báo một mảng A gồm nhiều phần tử, với A[i]=true theo nghĩa i là phần tử đợc chọn, A[i]=false theo nghĩa i là phần tử không đợc chọn
2- ứng dụng PP đánh dấu trong bài toán sắp xếp dãy số
Sắp xếp dữ liệu đóng vai trò rất quan trọng trong xữ lý thông tin ý nghĩa thực tiễn của sắp xếp là nhằm dễ dàng tìm kiếm thông tin cần thiết
Bài toán: Cho một dãy số gồm N phần tử (1<=N<=32766) Các phần tử ai của dãy là các số nguyên dơng, đôi một khác nhau (1<=ai <=32766) Hãy sắp xếp dãy số tăng dần
Ta thờng sử dụng thuật giải sắp xếp đơn giản để giải quyết bài toán này nh sau:
For i:=1 to N-1 do
For j:=i+1 to N do
If a[i]<a[j] then
Begin t:=a[i];
a[i]:=a[j]
a[j]:=t;
End;
Chơng trình biểu diễn của thuật toán:
const fi='sap1.inp';
fo='sap1.out';
type mmc=array[1 32766] of integer;
var f:text;
i,j,n,t:integer;
a:^mmc; ti:longint;
begin
ti:=meml[0:$46c];
new(a);
assign(f,fi);reset(f);
readln(f,n);
for i:=1 to n do read(f,a^[i]);
close(f);
for i:=1 to n-1 do
for j:=i+1 to n do
if a^[i]>a^[j] then
begin
t:=a^[i]; a^[i]:=a^[j]; a^[j]:=t;
end;
assign(f,fo);rewrite(f);
writeln(f,n);
for i:=1 to n do write(f,a^[i],' ');
close(f);
dispose(a);
writeln('Thoi gian thu hien ',(meml[0:$46c]-ti)/18.21:8:5);
Trang 2end.
Khi N bé, thuật toán trên là chấp nhận đợc Tuy nhiên, trong nhiều trờng hợp N lớn, chẳng hạn N=32766 phần tử, khi đó độ phức tạp của thuật toán là O(N2) máy sẽ thực hiện trong rất nhiều thời gian mới sắp xếp đợc dãy số (với N=20000, thời gian thực hiện khoảng
14 giây)
Để giải quyết đợc bài toán này khi N lớn trong một khoảng thời gian rất nhỏ, ta sử dụng kỹ thuật đánh dấu phần tử
Ta cần chú ý đến một giả thiết quan trọng trong bài toán đặt ra là “các phần tử đôi một khác nhau”, nghĩa là trong dãy không có phần tử nào trùng nhau Đối với bài toán sắp xếp có phần tử trùng nhau ta không thể sử dụng phơng pháp này
Phơng pháp:
Dữ liệu:
Sử dụng một mảng A gồm 32766 phần tử, các phần tử có kiểu boolean
ý nghĩa:
A[i]=true có nghĩa i là phần tử có trong dãy, A[i]=false có nghĩa i là phần tử không
có trong dãy
Thuật toán:
+ Khởi động mọi giá trị của A[] là False {Giống nh giả sử ban đầu mọi phần tử đều không thuộc dãy số}
+ Đọc từng phần tử của dãy số, giả sử số thứ j của dãy là X, ta đánh dấu phần tử A[X]=true {Xác nhận số X thuộc dãy số} Thực hiện đánh dấu cho đến khi đọc hết dãy số Khi đó ta thu đợc một mảng A[] trong đó A[i]=true tại các chỉ số i có giá trị bằng giá trị các phần tử trong dãy số
+ Duyệt từ đầu mảng đến cuối mảng, nếu vị trí vào có giá trị True thì ta xuất chỉ số
đó ra Kết quả ta đợc một dãy số đợc sắp xếp tăng dần
Chơng trình mẫu:
const fi='sap1.inp';
fo='sap2.out';
type mmcb=array[1 32766] of boolean;
var f:text;
n:word;
b:mmcb;
ti:longint;
procedure doc;
var i,x:word;
begin
fillchar(b,sizeof(b),false);
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,x);
b[x]:=true;
end;
close(f);
assign(f,fo);
Trang 3writeln(f,n);
for i:=1 to 32766 do
if b[i]=true then write(f,i,' ');
end;
begin
ti:=meml[0:$46c];
doc;
writeln('TG=',(meml[0:$46c]-ti)/18.21:8:4);
readln;
end.
Nhận xét:
Khi N=20000 chơng trình thực hiện trong 0.05giây Chơng trình này chạy nhanh gấp 280 lần so với chơng trình đã viết theo thuật toán đơn giản trên.
Rõ ràng, kỹ thuật đánh dấu phần tử có ý nghĩa rất quan trọng trong việc giảm thời gian thực hiện chơng trình
3-ứng dụng PP đánh dấu trong bài toán lọc dữ liệu
Lọc dữ liệu là một vấn đề có ý nghĩa to lớn trong xử lý thông tin ý nghĩa thực tiễn của lọc dữ liệu là nhằm loại bỏ các dữ liệu d thừa, không cần thiết, từ đó dễ dàng thu đợc thông tin cần tìm
Bài toán: Cho một dãy số gồm N phần tử (1<=N<=32766), trong đó các phần tử có kiểu nguyên nằm trong [1 32766] Hãy trích ra từ dãy số trên một tập con gồm nhiều phần
tử nhất sao cho các phần tử đôi một khác nhau
Ta thờng giải quyết bài toán trên theo thuật toán đơn giản nh sau:
+ Dùng một mảng B[] để lu các giá trị tìm đợc
+ Đọc từng phần tử của dãy số đã cho, giả sử số đọc đợc là X Kiểm tra xem X đã có trong B[] hay cha
+ Nếu cha có trong B[] thì đặt vào cuối cùng của B[]
Khi N bé, thuật toán trên có thể chấp nhận đợc Tuy nhiên, trong nhiều trờng hợp N rất lớn, chẳng hạn N=32766 phần tử, khi đó độ phức tạp của thuật toán là O(N2) máy sẽ thực hiện trong rất nhiều thời gian để lấy từng phần tử trong dãy để so sánh với các phần tử trong tập B[]
Để giải quyết đợc bài toán này khi N lớn trong một khoảng thời gian rất nhỏ, ta sử dụng kỹ thuật đánh dấu phần tử
Phơng pháp:
Dữ liệu:
Sử dụng một mảng B gồm 32766 phần tử, các phần tử có kiểu boolean
ý nghĩa:
B[i]=true có nghĩa i là phần tử ta sẽ chọn, B[i]=false có nghĩa i là phần tử ta không chọn
Thuật toán:
+ Khởi động mọi giá trị của B[] là False {Giống nh giả sử ban đầu ta cha chọn phần
tử nào cả}
+ Đọc từng phần tử của dãy số, giả sử số thứ j của dãy là X, ta đánh dấu phần tử B[X]=true {Xác nhận số X đợc chọn} Thực hiện đánh dấu cho đến khi đọc hết dãy số Khi
đó ta thu đợc một mảng B[] trong đó B[i]=true tại các chỉ số i mà ít nhất i xuất hiện một lần trong dãy đã cho
+ Duyệt từ đầu mảng đến cuối mảng B[], nếu vị trí nào có giá trị True thì ta xuất chỉ
số đó ra Kết quả ta đợc một tập các phần tử cần tìm
Chơng trình mẫu:
Trang 4const fi='tc.in1';
fo='tc.ou4';
nn=60000;
var n,a:word;
f:text;
k:array[1 nn] of boolean;
procedure doctep;
var i:word;
begin
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,a);
k[a]:=true;
end;
close(f);
end;
procedure xulivaxuat;
var i,d:word;
begin
assign(f,fo);
rewrite(f);
d:=0;
for i:=1 to nn do
if k[i]=true then d:=d+1;
writeln(f,d);
for i:=1 to nn do
if k[i]=true then write(f,' ',i);
close(f);
end;
BEGIN
doctep;
xulivaxuat;
END.
Nhận xét: Chơng trình này chạy nhanh gấp khoảng 300 lần so với chơng trình đã viết theo
thuật toán đơn giản trên
4-ứng dụng PP đánh dấu trong bài toán tìm giao của hai tập hợp
Xác định giao của hai tập hợp là một bài toán quan trọng trong toán học Trong thực tiễn, phép giao nhằm giúp ta xác định đợc nhóm thông tin chung nhất của nhiều nhóm thông tin
Bài toán:
Cho 2 tệp văn bản TEP1.INP và TEP2.INP chứa N số tự nhiên trong khoảng 1 M có thể trùng nhau Hãy tạo TEP3.OUT chứa các số có mặt trong cả hai tệp TEP1.INP và TEP2.INP sao cho các số đôi một khác nhau
Trang 5DLV DLR Dòng 1: Số N (1<=N<=32766)
Dòng 2: N số ai (1<=ai<=M<=32766)
Dòng 1 chứa các số tìm đợc
Ví dụ
7
5 7 1 3 5 2 7
6
3 5 1 2 1 19
1 3 2 5
Ta thờng giải quyết bài toán trên theo thuật toán đơn giản nh sau:
+ Dùng mảng A[] để lu các số trong tệp 1
+ Dùng mảng B[] để lu các số trong tệp 2
+ Lấy từng phần tử Xi trong A[], so sánh với lần lợt từng phần tử Yj trong B[] Nếu Xi
có trong B[] thì đem Xi đặt vào mảng C[]
+ Lấy từng phần tử Yj trong B[], so sánh với lần lợt từng phần tử Zk trong C[] Nếu Yj
có trong C[] thì đem Yj đặt vào mảng D[]
+ Xuất mảng D, ta thu đợc tập giao của hai tệp
Khi N bé, thuật toán trên có thể chấp nhận đợc Tuy nhiên, trong nhiều trờng hợp N rất lớn, chẳng hạn N=32766 phần tử, khi đó độ phức tạp của thuật toán là O(2N2) máy sẽ thực hiện trong rất nhiều thời gian để lấy từng phần tử trong A[] để so sánh với các phần tử trong B[]
Để giải quyết đợc bài toán này khi N lớn trong một khoảng thời gian rất nhỏ, ta sử dụng kỹ thuật đánh dấu phần tử nh sau:
Phơng pháp:
Dữ liệu:
Sử dụng hai mảng A và B gồm 32766 phần tử, các phần tử có kiểu boolean
ý nghĩa:
A[i]=true có nghĩa i là phần tử thuộc tệp 1, A[i]=false có nghĩa i là phần tử không thuộc tệp 1
B[i]=true có nghĩa i là phần tử thuộc tệp 2, B[i]=false có nghĩa i là phần tử không thuộc tệp 2
Thuật toán:
+ Khởi động mọi giá trị của A[] và B[] là False
+ Đọc từng phần tử của tệp 1, giả sử số đọc đợc của dãy là X, ta đánh dấu phần tử A[X]=true {Xác nhận số X thuộc tệp 1} Thực hiện đọc và đánh dấu cho đến khi đọc hết tệp 1
Khi đó ta thu đợc một mảng A[] trong đó A[i]=true tại các chỉ số i mà ít nhất i xuất hiện một lần trong tệp 1
+ Đọc từng phần tử của tệp 2, giả sử số đọc đợc của dãy là Y, ta đánh dấu phần tử B[Y]=true {Xác nhận số Y thuộc tệp 2} Thực hiện đọc và đánh dấu cho đến khi đọc hết tệp 2
Khi đó ta thu đợc một mảng B[] trong đó B[i]=true tại các chỉ số i mà ít nhất i xuất hiện một lần trong tệp 2
+ Duyệt từ đầu mảng đến cuối mảng A[] B[], nếu tại vị trí nào mà A[i] và B[i] có giá trị True thì ta xuất chỉ số đó ra Kết quả ta đợc một tập các phần tử cần tìm
Chơng trình mẫu:
const f1='tep1.inp';
f2='tep2.inp';
f3='tep3.out';
type mmc=array[1 32767] of boolean;
var n,i,j,a:longint;
k:mmc;
Trang 6procedure doctep;
begin
assign(f,f1);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,a);
k[a]:=true;
end;
close(f);
end;
procedure xulivaxuat;
begin
assign(fi,f3);
rewrite(fi);
assign(f,f2);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,a);
if k[a]=true then
begin
write(fi,' ',a);
k[a]:=false;
end;
end;
close(f);
close(fi);
end;
BEGIN
doctep;
xulivaxuat;
end.
Nhận xét: Chơng trình này chạy nhanh gấp 400 lần so với chơng trình đã viết theo thuật toán
đơn giản trên