Tải bản đầy đủ (.doc) (39 trang)

sang kien kinh nghiem ky nang trien khai giai thuat

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 (198.62 KB, 39 trang )

ĐỀ TÀI:
RÈN LUYỆN KỸ NĂNG TRIỂN KHAI GIẢI THUẬT CHO
HỌC SINH CHUYÊN TIN.
PHẦN I. ĐẶT VẤN ĐỀ
1. Lý do chọn đề tài.
Thời kỳ phát triển rực rỡ của lý thuyết Lập trình cấu trúc (1970 – 1985)
người ta thường nhắc đến công thức nổi tiếng của N. Wirth:
Chương trình = Cấu trúc dữ liệu + giải thuật.
Nhưng thực tế cho thấy vai trò của con người trong lập trình là rất lớn.
Nó liên quan tới phong cách, thói quen, tay nghề lập trình và thậm chí cả
yếu tố tâm lý lúc làm việc. Nó mang hàm lượng nghệ thuật nhiều hơn là bộ
quy tắc tiền định. Vì vậy, bây giờ người ta quan niệm:
Chương trình = Cấu trúc dữ liệu + giải thuật+ Nghệ thuật lập trình.
Một yếu tố quan trọng tác động lên nghệ thuật lập trình là kỹ năng
triển khai giải thuật.
Sai lầm phổ biến nhất mà các em học sinh hay mắc phải khi giải một
bài toán tin là ở phần chuẩn bị các em thường chỉ dừng lại bước đoán nhận
giải thuật rồi nôn nóng bắt tay ngay vào việc viết chương trình. Vì vậy
chương trình thường không hiệu quả, dễ mắc lỗi, mà việc tìm và sửa lỗi
nhiều khi rất khó khăn, mất thời gian, có khi phải mất thời gian ngang với
thời gian lập trình. Do vậy trong các kỳ thi HSG các em có thể đạt một số
kết quả nhất định, nhưng không cao, không tương xứng với khả năng và
công sức bỏ ra.
Bởi vậy, để học sinh đạt được kết quả cao trong các kỳ thi HSG Quốc
gia, Quốc tế cần phải rèn luyện cho các em tuân thủ tốt các bước thiết kế
và xây dựng chương trình, điều này không dễ chút nào, đòi hỏi giáo viên
phải kiên nhẫn, duy trì uốn nắn ngay từ khi các em bắt đầu với những bài
toán đơn giản và trong suốt quá trình học cho đến khi các em đi thi.

1



Với kinh nghiệm của giáo viên dạy chuyên và nhiều năm bồi dưỡng
HSG, tôi chọn đề tài “Rèn luyện kỹ năng triển khai giải thuật cho học sinh
chuyên Tin”. Rất mong nhận được các góp ý từ các đồng nghiệp.
2. Phạm vi nghiên cứu.
- Kiến thức: Kỹ năng triển khai thuật toán, tuân thủ chặt chẽ nguyên lý
“Trì hoãn việc tác động đến dữ liệu cụ thể”. Việc trì hoãn này duy trì
được càng lâu bao nhiêu thì càng tốt bấy nhiêu.
- Không gian: Đề tài được thực hiện tại trường THPT chuyên Phan Bội
Châu và tại các đợt tập huấn môn chuyên.
- Thời gian thực hiện: năm học 2011- 2012.
3. Mục tiêu đề tài.
- Đối với giáo viên:
• Phục vụ giảng dạy
• Phục vụ thi giáo viên giỏi.
• Bồi dưỡng HSG
- Đối với học sinh.
• Rèn luyện phong cách, thói quen lập trình hiệu quả.
• Ôn thi HSG.
4. Phương pháp nghiên cứu.
- Kinh nghiệm bản thân, thảo luận, sưu tầm tài liệu, thử nghiệm thực tế, rút
kinh nghiệm từ các tiết dạy trên lớp.

2


PHẦN II. NỘI DUNG
A. CƠ SỞ LÝ LUẬN.
Sau khi đọc đề bài, hai yêu cầu đầu tiên mà học sinh phải thực hiện là:
• Đoán nhận lớp giải thuật (quy hoạch động, tìm kiếm nhị phân,

loang theo chiều rộng, . . .),
• Trình bày sơ đồ triển khai giải thuật áp dụng với bài toán cụ thể
nhận được.
Đoán nhận lớp giải thuật không phải là chuyện quá khó. Mỗi lớp giải
thuật có một số đặc trưng nhận dạng. Các đặc trưng này thể hiện khá rõ
trong cách phát biểu bài toán.
Sai lầm của các em học sinh là thường chỉ dừng lại ở bước đoán nhận
lớp giải thuật và coi đó là toàn bộ phận chuẩn bị trước khi viết chương
trình. Việc trình bày sơ đồ triển khai giải thuật đối với bài toán cụ thể
nhận được quan trọng hơn nhiều. Các phần còn lại (lập trình và hiệu
chỉnh), học sinh có thể tự giải quyết, chúng ta chỉ phải kiểm soát, uốn nắn
để việc lập trình được thực hiện theo đúng sơ đồ triển khai đã trình bày.
Nếu học sinh có được thói quen và kỹ năng triển khai giải thuật tốt thì
các em sẽ đạt kết quả cao trong các kỳ thi HSG.
B. GIẢI QUYẾT VẤN ĐỀ.
Sơ đồ triển khai giải thuật bao gồm hai phần:
• Sơ đồ điều khiển,
• Sơ đồ tính toán.
Ở phần sơ đồ điều khiển, học sinh phải nêu các công việc theo trình tự xử
lý. Mỗi công việc sẽ tương ứng với một hàm hoặc thủ tục. Với mỗi công
việc, nếu nó còn khá phức tạp, chưa thể hiển nhiên thấy được cách tính thì
lại tiếp tục được triển khai thành dãy các công việc con. Quá trình triển
khai này được thực hiện cho đến khi mỗi công việc trở nên đủ đơn giản,
việc tính toán chỉ liên quan tới một hoặc một vài dữ liệu ban đầu hoặc dẫn
xuất trung gian.

3


Như vậy, ở phần sơ đồ điều khiển chúng ta chưa tác động đến dữ

liệu cụ thể, ngoại trừ có thể dùng đến một vài tham số về kích thước để tổ
chức quản lý chu trình. Cần phải quán triệt nguyên lý “Trì hoãn việc tác
động đến dữ liệu cụ thể”. Việc trì hoãn này duy trì được càng lâu bao
nhiêu thì càng tốt bấy nhiêu.
Như vậy, một sơ đồ điều khiển tốt sẽ dẫn dắt người lập trình tới một
hoặc một vài phép đơn giản tác động lên giá trị của một vài dữ liệu nào đó.
Phép hoặc các phép xử lý này được gọi là phép xử lý đơn vị (cho giải thuật
đang triển khai) và dữ liệu mà nó tác động - dữ liệu đơn vị.
Toàn bộ việc mô tả giải thuật phải xoay quanh đơn vị xử lý. Ta sẽ có
được một sơ đồ điều khiển cực kỳ linh hoạt, dễ dàng cải tiến, nâng cấp
chương trình để có một phiên bản làm việc hiệu quả hơn.
Có hai tiêu chuẩn đánh giá chất lượng của việc trình bày một giải
thuật: tiêu chuẩn định tính và tiêu chuẩn định lượng.
Tiêu chuẩn định tính: mọi học sinh còn lại, sau khi nghe trình bày
giải thuật, phải tự tin là có khả năng viết được chương trình theo giải thuật
đang thảo luận, đúng theo các bước đã nêu ngay cả khi chưa hiểu rõ lắm về
giải thuật, chưa tin tưởng rằng đây là giải thuật đúng.
Tiêu chuẩn định lượng: giải thuật phải được trình bày trên ngôn ngữ
dữ liệu và các phép xử lý đã nêu. Mọi đại lượng đều phải được đặt tên, chỉ
rõ các thuộc tính, giá trị đầu (nếu có) là gì, việc tính toán hoặc cập nhật lại
giá trị của các biến đó được thực hiện như thế nào, . . . Tóm lại, trong trình
bày giải thuật không xuất hiện các cụm từ “đại lượng bên trái”, “đại lượng
bên phải”, “cứ như thế cho đến hết”, v.v. . . Thay vào đó phải là “ai-1”,
“ci,j+1”, “với mọi i từ j đến n” v.v. . . Khi nói tới một mảng phải chỉ rõ mảng
tên là gì, các chỉ số thay đổi từ đâu tới đâu, kiểu dữ liệu thế nào, dùng để
lưu trữ gì. Tương tự như vậy đối với chu trình: loại chu trình, điều kiện lặp
hay ra khỏi chu trình hoặc tham số điều khiển chu trình và các biên của
tham số.
4



Kỹ năng trình bày giải thuật là việc khó nhất trong công tác đào tạo
bồi dưỡng học sinh năng khiếu. Việc rèn luyện kỹ năng này phải được bắt
đầu từ rất sớm, bắt đầu từ những bài toán đơn giản và cần duy trì uốn nắn
cho đến phút cuối cùng trước khi đi thi.
C. MỘT SỐ VÍ DỤ VỀ VIỆC RÈN LUYỆN KỸ NĂNG TRIỂN
KHAI GIẢI THUẬT.
VÍ DỤ 1. XẾP SÁCH.
Thư viện trường vừa được bổ sung một khối lượng lớn sách. Người
thủ thư phân loại sách và xếp chúng thành từng
chồng trên một bàn cao. Mặt bàn được chia
thành lưới n× n ô (1 ≤ n ≤ 500). Mỗi chồng
sách chiếm vừa khít một ô. Có thể có các ô
trống trên bàn. Các cuốn sách được dán dấu
hiệu phân loại ở gáy và các phía xung quanh.
Sinh viên trẻ nhất trường được giao nhiệm vụ
ghi các sách mới vào phiếu tra cứu của thư viện. Ngán ngẩm nhìn đống
sách ngồn ngộn, anh ta đi vòng quanh bàn, nhìn đống sách theo các hướng
song song với cạnh của bàn, đọc các phiếu phân loại của từng chồng nhìn
thấy được. Một chồng sách có thể được nhìn thấy nếu giữa người sinh viên
và chồng sách không có chồng nào cao hơn hoặc bằng theo hướng nhìn.
Theo tình huống ở hình bên, người sinh viên không thấy được chồng sách
ở ô (2, 2). Các ô (2,3), (3, 3) và (3, 4) là ô trống.
Yêu cầu: Hãy xác định số chồng sách mà người sinh viên nhìn thấy được.
Dữ liệu: Vào từ file văn bản BOOKS.INP:



Dòng đầu tiên chứa số nguyên n,
Dòng thứ i trong n dòng sau chứa n số nguyên xác định độ

cao các chồng sách trong hàng, mỗi độ cao có giá trị không
vượt quá 1 000.

5


Kết quả: Đưa ra file văn bản BOOKS.OUT một số nguyên – số chồng sách
nhìn thấy được.
Ví dụ:
BOOKS .INP
4

Phân tích:

BOOKS .OUT
12

3321

• Lớp

4102
3200

o

3121

giải
thuật:

Nhận
dạng đối

tượng.
o Nguyên lý: đánh dấu đối tượng đã nhận dạng.
• Cấu trúc dữ liệu: Cần bản đồ đánh dấu
d:array[1..500,1..500] of byte;
d[i,j]=1 nếu chồng sách tại vị trí (i,j) nhìn thấy được.
d[i,j]=0 nếu không nhìn thấy.
• Thiết kế chương trình:
o Bước 1:
 Liệt kê các công việc, các bước bắt buộc phải thức hiện,
 Phân rã việc xử lý thành dãy các công việc đơn giản hơn hoặc tác
động lên một bộ phận dữ liệu (giảm chiều, giảm kích thước bài
toán),
 Nguyên lý: tháo gỡ khó khăn chứ không phải đẩy lùi khó khăn
xuống mức sau!
Sản phẩm chương trình ở bước 1 có dạng:
Program VD1_ThKe_CT;
Const tfi

=

’BOOKS.INP’;

tfo

=

’BOOKS.OUT’;


b

:

array[1..500,1..500] of integer;

d

:

array[1..500,1..500] of byte;

Var

6


n,imx:

integer;

f,g

text;

:

BEGIN
Mofile;

Nhap;
Chbi;
Qsat_dong;
Qsat_cot;
Ghnhkq;
Dongfile;
END.
Qsat_dong và Qsat_cot là hai gần giống nhau, chỉ khác nhau ở cách
điều khiển tham số và chúng tương ứng với thao tác nêu trong đầu bài. Chi
phí thiết kế chương trình ở bước 1 gần như bằng 0 (ta chỉ mới phát biểu lại
bài toán đã cho!).
o Bước 2: chi tiết hóa các công việc ở bước 1. Ở đây chúng ta chỉ
quan tâm sâu đến Qsat_dong và Qsat_cot.
Procedure Qsat_dong;
Var

i

:

integer;

Begin
For i:=1 to n do
begin
Qs_d_tp(i); {quan sat dong i tu trai sang phai}
Qs_d_pt(i) {quan sat dong i tu phai sang trai}
End
End;
Các thủ tục Qs_d_tp(i) và Qs_d_pt(i) xác định các công việc quan sát

dòng i từ trái sang phải và quan sát dòng i từ phải sang trái. Chúng ta cũng
gần như chưa làm gì cả, nhưng đã đưa được bài toán hai chiều, quan sát
7


bốn hướng về bài toán một chiều, quan sát từ một hướng. Nhiệm vụ cần
thực hiện trở nên đơn giản và rõ ràng hơn nhiều. Trong thủ tục trên ta mới
sử dụng có một dữ liệu vào (giá trị n).
o Bước 3: Chi tiết hóa việc xử lý một dòng và một hướng về việc xử
lý một phần tử b[i,j].
h: là giá trị phần tử cao nhất từ đầu hàng tới vị trí xét.
jmx: là chỉ số cột ứng với phần tử cao nhất trong hàng.
Procedure Qs_d_tp(x:integer);
Var

j, h : integer;

Begin
h:=0; jmx:=n;
for j:= 1 to n do
if b[x,j]>h then
begin d[x,j]:=1; h:= b[x,j]; jmx:=j end
End;
Khi quan sát hàng từ phải sang trái ta chỉ cần xử lý đến vị trí cột cao
nhất trong hàng (vị trí jmx):
Procedure Qs_d_pt(x:integer);
Var j,h:integer;
Begin
h:=0;
for j:= n downto jmx do

if b[x,j]>h then
begin d[x,j]:=1; h:= b[x,j] end
End;
Việc xử lý quan sát theo cột được thực hiện tương tự, chỉ cần đảo lại
vị trí chỉ số của b.
Tuy thủ tục ghi nhận kết quả là đơn giản nhưng chúng ta vẫn dẫn
xuất nó ở đây:
8


Procedure Ghnhkq;
Var

i,j : integer;
res : longint;

Begin
res:= 0;
For i:= 1 to n do
For j:=1 to n do res:= res+d[i,j];
asign(f,tfo); rewrite(f);
writeln(f,res);
close(f)
End;
Không có gì khó khăn trong việc xây dựng các thủ tục còn lại để có
chương trình hoàn thiện.
Như vậy, toàn bộ mọi nỗ lực xử lý chỉ tập trung vào một phần tử
b[i,j] và từ một hướng quan sát và tất cả gói gọn trong một câu lệnh.
**** Toàn bộ chương trình****
Program VD1_ThKe_CT;

Const

fi

=

’BOOKS.INP’;

fo

=

’BOOKS.OUT’;

b

:

array[1..500,1..500] of integer;

d

:

array[1..500,1..500] of byte;

Var

n, jmx :


integer;

f, g

text;

:

9


Procedure Mofile;
Begin
Assign(f,fo);Reset(f);
Assign(g,fo);

Rewrite(g);

End;
Procedure Dongfile;
Begin
Close(f);
Close(g);
End;
Procedure Nhap;
Var

i, j

:


Integer;

Begin
Readln(f,n);
For i := 1 to n do
For j := 1 to n do Read(f, b[i,j]);
End;
Procedure Qs_d_tp(x:integer);
Var j,h : integer;
Begin
h:=0; jmx:=n;
for j:= 1 to n do
if b[x,j]>h then
begin d[x,j]:=1; h:= b[x,j]; jmx:=j end
End;
Procedure Qs_d_pt(x:integer);
Var j,h:integer;
Begin
h:=0;
10


for j:= n downto jmx do
if b[x,j]>h then
begin d[x,j]:=1; h:= b[x,j] end
End;
Procedure Qsat_dong;
Var i : integer;
Begin

For i:=1 to n do
begin
Qs_d_tp(i); {quan sat dong i tu trai sang phai}
Qs_d_pt(i)

{quan sat dong i tu phai sang trai}

end
End;
Procedure Qs_c_td(x:integer);
Var i,h : integer;
Begin
h:=0; jmx:=n;
for i:= 1 to n do
if b[i,x]>h then
begin d[i,x]:=1; h:= b[i,x]; jmx:=i end
End;
Procedure Qs_c_dt(x:integer);
Var i,h:integer;
Begin
h:=0;
for i:= n downto jmx do
if b[i,x]>h then
begin d[i,x]:=1; h:= b[i,x] end
End;
11


Procedure Qsat_cot;
Var i:integer;

Begin
For i:=1 to n do
begin
Qs_c_td(i); {quan sat cot i tu tren xuong duoi}
Qs_c_dt(i)

{quan sat cot i tu duoi len tren}

end
End;
Procedure Ghnhkq;
Var

i,j : integer;
res : longint;

Begin
res:= 0;
For i:= 1 to n do
For j:=1 to n do res:= res+d[i,j];
writeln(g,res);
End;
BEGIN
Mofile;
Nhap;
Chbi;
Qsat_dong;
Qsat_cot;
Ghnhkq;
Dongfile;

END.

12


VÍ DỤ 2. KHOẢNG CÁCH HAMMING (Đề thi HSG Quốc Gia 2012)
Các phân tử trong tế bào sinh học được cấu tạo từ 4 loại nuclêôtit cơ
bản ký hiệu bởi các chữ cái A, X, T, G. Mỗi gen di truyền được cấu tạo
thành bởi một chuỗi các nuclêôtit với độ dài được tính bằng số lượng
nuclêôtit. Ví dụ, AXXTTGAT là một gen có độ dài 8.
Trong một chuyến đi khảo sát, Giáo sư Altein phát hiện ra một gen
lạ gồm n nuclêôtit được xếp trên một vòng tròn. Ngay lập tức, Giáo sư
Altein dự định tiến hành so sánh gen lạ này với một số gen mẫu đang lưu
trữ nhằm tìm hiểu xem mẫu gen này có gần gũi với loại gen mẫu nào đã
được biết. Trong sinh học để đo độ khác biệt giữa hai mẫu gen người ta
thường tính khoảng cách Hamming giữa chúng. Khoảng cách Hamming
giữa hai gen cùng độ dài được định nghĩa là số lượng vị trí mà tại đó hai
gen chứa các nuclêôtit khác nhau. Ví dụ, hai gen AGGTT và TGATT có
khoảng cách Hamming bằng 2 do 2 nuclêôtit ở các vị trí 1 và 3 của chúng
là khác nhau. Do các gen mẫu được sử dụng đều có độ dài m (m ≤ n) và có
cấu trúc thẳng, trong khi gen lạ lại có độ dài n và có cấu trúc vòng nên
Giáo sư Altein đã định nghĩa khoảng cách Hamming giữa một gen mẫu và
gen lạ là số nhỏ nhất trong số các khoảng cách Hamming giữa gen mẫu và
những đoạn gen gồm m nuclêôtit liên tiếp theo chiều kim đồng hồ trong
gen lạ.
Yêu cầu: Cho k gen mẫu, hãy xác định gen mẫu với khoảng cách
Hamming đến gen lạ là nhỏ nhất và đưa ra khoảng cách tìm được.
Ràng buộc: 50% số tests ứng với 50% số điểm của bài có n ≤ 100.
Dữ liệu: Vào từ file văn bản HAMMING.INP:
• Dòng thứ nhất chứa ba số nguyên dương n, m, k (m ≤ n ≤ 1000; k ≤

100).
• Dòng thứ hai chứa xâu độ dài n là dãy các nuclêôtit của gen lạ được
liệt kê theo chiều kim đồng hồ bắt đầu từ một vị trí nào đó.

13


• Dòng thứ i trong số k dòng tiếp theo chứa xâu độ dài m biểu diễn
gen mẫu thứ i.
Kết quả: Đưa ra file văn bản HAMMING.OUT:
• Ghi ra một số nguyên là khoảng cách Hamming nhỏ nhất tìm được.
Ví dụ:
HAMMING.INP
732

HAMMING.OUT
1

GTAAXXT
GAT
TTT

Phân tích:
• Lớp giải thuật:
o So sánh mẫu
o Nguyên lý: Tìm cực trị trong những lần đối sánh mẫu với những
mẫu đã được mã hóa
• Cấu trúc dữ liệu:
List= array[1..2000] of longint;
xau:array[1..maxK] of list;

vt:array['A'..'Z'] of longint;
vt[ch] là mã hóa dạng số của loại nucleotit “ch” (A,T,G,X)
xau[i] là mã hóa dạng dãy số (dạng list) của gen.
• Thiết kế chương trình
o Bước 1:
 Liệt kê các công việc, các bước bắt buộc phải thực hiện,
 Phân rã việc xử lý thành dãy các công việc đơn giản hơn hoặc tác
động lên một bộ phận dữ liệu (giảm kích thước bài toán),
 Nguyên lý: tháo gỡ khó khăn chứ không phải đẩy lùi khó khăn
xuống mức sau!
14


Sản phẩm chương trình ở bước 1 có dạng:
Program VD2_ThKe_CT;
Const

Type
Var

fi

=

’HAMMING.INP’;

Fo

=


’HAMMING.OUT’;

maxK=

102;

vc

maxlongint div 2;

=

list=array[1..2000] of longint;
S

:

list;

N,M,K

:

longint;

Xau

:

array[1..maxK] of list;


Res

:

longint;

Vt

:

array['A'..'Z'] of longint;

f, g

:

text;

BEGIN
Mofile;
Nhap_mahoa;
SoSanhmau;
Ghnhkq;
Dongfile;
END.
o Bước 2: Chi tiết hóa các công việc ở bước 1. Ở đây ta chỉ quan tâm
sâu đến Nhap_mahoa; và SoSanhmau;
Procedure Nhap_ mahoa; { Nhap du lieu kem theo ma hoa cac ky tu}
var


i, j

:

longint;

ch

:

char;

Begin
vt['A']:=1; vt['G']:=2; vt['X']:=3; vt['T']:=4;
readln(f,N,M,K);
Mhgenla;

{doc va ma hoa gen la}
15


Mhgenmau;

{doc va ma hoa gen mau}

end;
Việc mã hóa để chuyển từ so sánh chuỗi hoặc các kí tự sang so sánh
số sẽ cải thiện phần nào đó tốc độ của chương trình.
Procedure SoSanhmau;

Var i, j, sl, x

:

{ Tiến hành so sánh và tìm cực trị }
longint;

Begin
for i:=1 to N do s[i+N]:=s[i]; {vi gen la la vong nen ta gap doi cho de
xu ly}
res:=vc;
for i:=1 to K do
Begin
for j:=1 to N do
Begin
Sl:=SoHamming(i, j); {tinh so Hamming gen mau i va gem la bat
dau tu j}
res:=min(res,sl);
if res=0 then exit;
end;
end;
end;
Hàm SoHamming(i, j) tính số Hamming của 2 gen có độ dai m là gen
mẫu i và gen lạ. Như vậy chúng ta đã chuyển từ bài toán tính số Hamming
của nhiều cặp gen thành tính số Hamming của 1 cặp gen. Nhiệm vụ cần
thực hiện trở nên đơn giản và rõ ràng hơn nhiều. Trong thủ tục trên ta mới
sử dụng có hai dữ liệu vào (giá trị k và n).
o Bước 3: Chi tiết hóa việc xử lý dãy về xử lý một phần tử s[i],
xau[i,j].


16


Procedure Mhgenla;
Begin
for i:=1 to N do

{doc tung ky tu trong gen la va ma hoa}

Begin
read(f,ch);
s[i]:=vt[ch];

{ S[i] la ma hoa ky tu thu i cua gen la }

end;
readln(f);
End;
Procedure Mhgenmau;
Begin
for i:=1 to k do

{duyet tung gen mau}

Begin
for j:=1 to M do

{doc tung ky tu trong gen mau va ma hoa}

Begin

read(f,ch);
xau[i,j]:=vt[ch]; { Xau[i,j] la ma hoa ky tu thu j cua gen mau i}
end;
readln(f);
end;
End;
function

SoHamming(i, j :longint):longint;

Begin
sl:=0;
for x:=1 to M do
Begin
sl:=sl+ord(s[x+j-1]<>xau[i,x]);
end;
End;
17


Tốc độ chương trình nhanh hay chậm cũng phụ thuộc vào việc ta xử
lý so sánh để tính số Hamming như thế nào. Ở đây kết hợp với mã hóa dữ
liệu việc so sánh chỉ gói gọn trong 1 câu lệnh.
****Toàn bộ chương trình****
Program

VD2_ThKe_CT;

uses math;
Const


Type
Var

fi

=

’HAMMING.INP’;

Fo

=

’HAMMING.OUT’;

maxK=

102;

vc

maxlongint div 2;

=

list=array[1..2000] of longint;
S

:


list;

N,M,K

:

longint;

Xau

:

array[1..maxK] of list;

Res

:

longint;

Vt

:

array['A'..'Z'] of longint;

f, g

:


text;

procedure Mofile;
Begin
assign(f,fi); reset(f); assign(g,fo); rewrite(g);
end;
procedure Dongfile;
Begin
close(f); close(g);
end;

18


Procedure Mhgenla;
Begin
for i:=1 to N do

{doc tung ky tu trong gen la va ma hoa}

Begin
read(f,ch);
s[i]:=vt[ch];

{ S[i] la ma hoa ky tu thu i cua gen la }

end;
readln(f);
End;

Procedure Mhgenmau;
Begin
for i:=1 to k do

{duyet tung gen mau}

Begin
for j:=1 to M do

{doc tung ky tu trong gen mau va ma hoa}

Begin
read(f,ch);
xau[i,j]:=vt[ch]; { Xau[i,j] la ma hoa ky tu thu j cua gen mau i}
end;
readln(f);
end;
End;
Procedure Nhap_ mahoa; { Nhap du lieu kem theo ma hoa cac ky tu}
var i, j : longint;
ch : char;
Begin
vt['A']:=1; vt['G']:=2; vt['X']:=3; vt['T']:=4;
readln(f,N,M,K);
Mhgenla;

{doc va ma hoa gen la}

Mhgenmau


{doc va ma hoa gen mau}
19


end;
Function

SoHamming(i, j :longint):longint;

Begin
sl:=0;
for x:=1 to M do
Begin
sl:=sl+ord(s[x+j-1]<>xau[i,x]);
end;
sohamming:=sl;
End;
Procedure SoSanhmau;
Var i, j, sl, x

:

{ Tiến hành so sánh và tìm cực trị }
longint;

Begin
for i:=1 to N do s[i+N]:=s[i]; {vi gen la la vong nen ta gap doi cho de
xu ly}
res:=vc;
for i:=1 to K do

Begin
for j:=1 to N do
Begin
Sl:=SoHamming(i, j); {tinh so Hamming gen mau i va gem la bat
dau tu j}
res:=min(res,sl);
if res=0 then exit;
end;
end;
end;
procedure ghinhkq;
Begin
20


writeln(g,res);
end;
Begin
Mofile;
Nhap;
SoSanhmau;
Ghinhkq;
Dongfile
End.
VÍ DỤ 3. HÀNH TRÌNH DU LỊCH

(Đề thi HSG Quốc Gia 2012)

Công ty du lịch tư nhân Travel chuyên tổ chức các tour du lịch nội
địa. Có n thành phố nằm trong phạm vi khai thác của công ty. Các thành

phố được đánh số từ 1 đến n. Có m cặp thành phố có đoạn đường hai chiều
trực tiếp nối chúng. Để đáp ứng yêu cầu của khách hàng trong các kỳ nghỉ
ngắn hạn, công ty chỉ khai thác các tour đi vòng quanh 4 thành phố theo
các đoạn đường trực tiếp nối chúng. Để chắc chắn có thể khai thác những
tour như vậy, công ty tiến hành khảo sát xem liệu có 4 thành phố nào tạo
thành một hành trình khép kín xuất phát từ một thành phố đi qua 3 thành
phố còn lại, mỗi thành phố đúng một lần và quay về thành phố xuất phát
hay không.
Yêu cầu: Hãy giúp công ty kiểm tra xem có tồn tại hành trình nào như vậy
hay không.
Ràng buộc: 50% số tests ứng với 50% số điểm của bài có n ≤ 500.
Dữ liệu: Vào từ file văn bản TRAVEL.INP:


Dòng thứ nhất chứa hai số nguyên dương n,m (n ≤ 10000; m ≤
200000)



Dòng thứ i trong số m dòng tiếp theo chứa 2 số là chỉ số 2 thành phố
có đoạn đường trực tiếp nối chúng
21


Kết quả: Đưa ra file văn bản TRAVEL.OUT:


Ghi ra 4 số nguyên dương theo thứ tự là 4 thành phố trên một hành
trình tìm được hoặc ghi số -1 nếu câu trả lời là phủ định.


Ví dụ:
TRAVEL.INP
5 6
1

2

1

5

2

3

2

5

3

4

TRAVEL.OUT
4523

4
5
Phân tích:
• Lớp giải thuật:

o Duyệt
o Nguyên lý: Giảm bớt các trường hợp duyệt bằng cách tiếp cận theo
dạng tựa cây
• Cấu trúc dữ liệu:
- Đồ thị biểu diễn bằng danh sách liên kết:
dsk:array[1..maxN] of link;
- Mảng lưu số cách đến được nút i từ gốc:
d:array[1..maxN] of longint;
-

Mảng lưu 2 cha trực tiếp của một nút i:
kq:array[1..maxN,1..2] of longint;

• Thiết kế chương trình
o Bước 1:
 Liệt kê các công việc, các bước bắt buộc phải thực hiện,

22


 Phân rã việc xử lý thành dãy các công việc đơn giản hơn hoặc tác
động lên một bộ phận dữ liệu (giảm kích thước bài toán),
 Nguyên lý: tháo gỡ khó khăn chứ không phải đẩy lùi khó khăn
xuống mức sau!
Sản phẩm chương trình ở bước 1 có dạng:
Program VD3_ThKe_CT;
Const fi

=


'TRAVEL.INP';

fo

=

'TRAVEL.OUT';

Type

maxN =

10000;

link

=

^node;

node

=

record

point

:


longint;

next

:

link;

f, g

:

Text;

dsk

:

array[1..maxN] of link;

N, m

:

longint;

d

:


array[1..maxN] of longint;

kq

:

array[1..maxN,1..2] of longint;

end;
Var

Begin
Mo_file;
Nhap;
Tim_hanhtrinh;
Ghinhkq;
Dong_file
End.
o Bước 2: Chi tiết hóa các công việc ở bước 1. Ở đây ta chỉ quan tâm
sâu đến Tim_hanhtrinh;

23


Procedure
Var

Tim_hanhtrinh;

i, j, k, x, sl


:

longint;

pp, pp2

:

link;

Begin
for i:=1 to N do
Begin
Khoitao;
Duongtu(i);

{khoi tao truoc khi xet moi dinh i}
{tim chu trinh 4 dinh xuat phat tu i va tro ve i}

end;
end;
Thủ tục Duongtu(i) sẽ thực hiện xử lý lần lượt với mỗi nút i. Chúng
ta cũng gần như chưa làm gì cả, nhưng đã đưa được bài toán tìm với n
phần tử về bài toán tìm với 1 phần tử. Nhiệm vụ cần thực hiện trở nên đơn
giản hơn. Trong thủ tục trên ta mới sử dụng có một dữ liệu vào (giá trị n).
o Bước 3: Chi tiết hóa việc xử lý đường đi từ đỉnh i.
Ở đây, với mỗi nút i, coi nút i là gốc. Từ gốc i sẽ lần lượt tìm đến các
con bậc 1 là J, với mỗi J sẽ tìm đến các con bậc 2 là K. Với mỗi con k bậc
2, mỗi lần thăm sẽ đánh dấu lại là đã thăm một lần và lần thăm đó là thông

qua con J bậc 1. Khi đó nếu như có 2 lần thăm con K bậc 2 sẽ dẫn tới tồn
tại 2 con bậc 1 là J1 và J2 khác nhau sao cho có cung nối từ I  J1  K
và từ I  J2  K. Suy ra 4 đỉnh I, J1, K, J2 sẽ tạo thành một chu trình
khép kín theo như yêu cầu của bài toán.
Procedure

Duongtu(i:longint);

Begin
pp:=dsk[i];
while pp<>nil do
Begin
j:=pp^.point;

{j la con bac 1 cua i}

Conbac2(i);
24


pp:=pp^.next;
end;
End;
Thủ tục Conbac2(i); sẽ duyệt qua các con bậc 2 cuả i .
o

Bước 4: Chi tiết hóa việc duyệt con bậc 2 để ghi nhận và kiểm tra
kết quả.

Procedure


Conbac2(i: longint);

Begin
pp2:=dsk[j];
while pp2<>nil do
Begin
k:=pp2^.point;
if k<>i then luu_kiemtra(k);
pp2:=pp2^.next;;
end;
End;
Thủ tục luu_kiemtra(k); để xét với mỗi con K bậc 2 của i, ta sẽ
dùng dữ liệu để dánh dấu giúp cho việc duyệt qua lại tốn ít thời gian và có
thể lấy được ngay kết quả.
o Bước 5: Chi tiết hóa việc ghi nhận con K bậc 2 của i và kiểm tra
kết quả.
Procedure

Luu_kiemtra(k:longint);

Begin
inc(d[k]);

{tang so lan tham cua dinh k}

kq[k,d[k]]:=j;

{duong di den k qua j}


if d[k]=2 then

{ neu k da duoc tham 2 lan thi xuat kq}

Ghiduongdi;
end;

25


×