Ngăn xếp được sử dụng rấtnhiều trong việc giải quyết các bài toán về đồ thị trong các đề thi học sinh giỏi.. Vì vậy tôichọn chuyên đề này để giúp học sinh có cái nhìn tổng quan hơn về ng
Trang 1TRƯỜNG THPT CHUYÊN TỈNH LÀO CAI
TỔ TOÁN - TIN
-*@* -CHUYÊN ĐỀ NGĂN XẾP (STACK) VÀ HÀNG ĐỢI (QUEUE)
NĂM HỌC: 2015 - 2016
Trang 2A, MỞ ĐẦU
1 Lý do chọn đề tài
Ngăn xếp và hàng đợi là hai kiểu dữ liệu trừu tượng rất quan trọng và được sửdụng nhiều trong thiết kế thuật toán Về bản chất, ngăn xếp và hàng đợi là danh sáchtức là một tập hợp các phần tử cùng kiểu có tính thứ tự Ngăn xếp được sử dụng rấtnhiều trong việc giải quyết các bài toán về đồ thị trong các đề thi học sinh giỏi Tuynhiên trong quá trình giảng dạy tôi thấy học sinh vẫn còn khó khăn trong việc phântích bài toán để có thể áp dụng được thuật toán và cài đặt giải bài toán Vì vậy tôichọn chuyên đề này để giúp học sinh có cái nhìn tổng quan hơn về ngăn xếp và ứngdụng của ngăn xếp trong giải các bài toán cụ thể
2 Mục đích của đề tài
Về nội dung kiến thức ngăn xếp và hàng đợi đã có rất nhiều tài liệu đề cập đến, trongchuyên đề này chúng tôi chỉ tổng hợp lại các nội dung kiến thức đã có và chủ yếu làđưa vào áp dụng để giải một số bài toán cụ thế, để làm tài liệu tham khảo cho họcsinh và giáo viên trong quá trình học tập và giảng dạy các đội tuyển học sinh giỏi
Trang 3B NỘI DUNG
I, DỮ LIỆU KIỂU NGĂN XẾP (STACK)
1, Khái niệm:
Stack là một kiểu danh sách tuyến tính đặc biệt mà phép
bổ sung và loại bỏ luôn thực hiện ở một đầu gọi là đỉnh (Top)
Có thể hình dung nó như cơ cấu của một hộp chứa đạn súng
trường hoặc súng tiểu liên Khi lắp đạn hay lấy đạn ra cũng chỉ
ở đầu hộp Viên đạn vừa lắp vào sẽ ở trên đỉnh hộp và viên đạn
lắp vào đầu tiên sẽ ở đáy hộp (Bottom) Viên đạn nạp vào sau
cùng lại là viên đạn bắn đầu tiên
Với nguyên tắc hoạt động của stack “vào sau ra trước” nên nó còn được gọi với tên danhsách kiểu LIFO (Last - In - First - Out)
2, Cài đặt stack
Ta dùng mảng Stack[I Nmax] mà ″đáy″ của Stack là ở đầu tức chỉ số là 1 Việc đưa vào (Push) hay lấy ra (Pop) được thực hiện phần đuôi của mảng nhờ một con trỏ P Các thao tác đưa vào hay lấy ra đó ứng với các thủ tục hàm thích hợp Giả sử Stack chứa các phần
tử là các số nguyên thì ta sẽ có các thủ tục và hàm sau:
3, Các phép xử lý trên stack
a. Khởi tạo stack rỗng: P := 0;
b. Kiểm tra một stack có rỗng hay không:
Function StackEmpty:Boolean; {Kiểm tra stack có rỗng không}
d. Thủ tục đẩy một phần tử vào stack:
Procedure Push (N:Integer); {Đưa số N vào Stack}
Trang 4a, Khử đệ quy thuật toán sắp xếp Quicksort;
b, Chuyển biểu thức từ dạng trung tố sang dạng hậu tố
Để minh hoạ ta xét biểu thức trung tố sau đây: 7 + 2 * 3 Khi đọc biểu thức này từtrái sang phải, giá trị 7 được hiển thị ngay lập tức Tiếp theo là toán tử +, nhưng nó được
Trang 5lưu trữ vì toán hạng bên phải của nó chưa được hiển thị, vì vậy nó được đẩy vào ngăn xếpcác toán tử:
Đầu ra Ngăn xếp
Tiếp theo là toán hạng 2 được đọc và được hiển thị Lúc này nó phải được xácđịnh là toán hạng bên phải của toán tử + hay là toán hạng bên trái của toán tử tiếp theo
Để xác định điều này ta so sánh toán tử + ở đỉnh ngăn xếp với toán tử tiếp theo * Bởi vì
* được ưu tiên hơn +, toán hạng 2 là toán hạng bên trái của toán tử * Vì vậy ta đẩy * vàongăn xếp và tìm toán hạng bên phải của nó:
Thuật toán chuyển từ dạng trung tố sang dạng RPN:
1 Khởi động một ngăn xếp rỗng các toán tử
2 While <không xảy ra lỗi và chưa đạt đến kết thúc của biểu thức trung tố> do
a Đọc phần tử x (hằng số, biến số, toán tử số học, các dấu ngoặc trái và ngoặc phải)
tiếp theo trong biểu thức trung tố
b Nếu phần tử x là:
- Dấu ngoặc trái: đẩy nó vào ngăn xếp
- Dấu ngoặc phải: lấy ra và hiển thị các phần tử của ngăn xếp cho đến khi dấungoặc trái được đọc Nếu ngăn xếp rỗng thì xảy ra lỗi
- Toán tử: nếu ngăn xếp rỗng hay x được ưu tiên hơn phần tử ở đỉnh ngăn xếp, đẩy x vào ngăn xếp.
Trang 6Nếu khác, lấy ra và hiển thị phần tử ở đỉnh ngăn xếp; Lặp lại việc so sánh x với
phần tử ở đỉnh ngăn xếp (Dấu ngoặc trái được xem ưu tiên thấp hơn các toántử)
- Toán hạng: hiển thị nó
3 Khi đạt đến kết thúc của biểu thức trung tố, lấy ra và hiển thị các phần tử của ngănxếp cho đến khi ngăn xếp rỗng
Cài đặt: chương trình này giả sử các toán hạng, toán tử chỉ gồm 1 kí tự và giả sử biểu
thức trung tố là hợp lệ và chỉ kiểm tra rất ít tính đúng đắn của biểu thức trung tố
stack : array[1 MaxSize] of char;
Function Pop : char;
Function Priority(operator : char) : integer;
{ ham tra lai do uu tien cua cac toan tu }
Trang 8write('Vao bieu thuc dang trung to: '); readln(infix);
infix := infix + EndMask;
II, DỮ LIỆU KIỂU HÀNG ĐỢI (QUEUE)
1 Khái niệm hàng đợi (Queue)
Khác với stack, queue là một danh sách tuyến tính mà phép bổ sung thực hiện ở một đầu,gọi là lối sau (rear) và phép loại bỏ thực hiện ở một đầu khác, gọi là lối trước (front)
Như vậy cơ cấu của queue giống như một hàng đợi (như là một hàng người chờ tính tiền
ở siêu thị, một dãy các máy bay chờ hạ cánh ở một sân bay, ) vào ở một đầu, ra ở đầukhác, nghĩa là vào trước thì ra trước Vì vậy queue được gọi là danh sách kiểu FIFO (First
- In - First - Out)
Quầy bán vé
Lối ra
Trang 92 Cài đặt queue
Có thể dùng mảng làm cấu trúc lưu trữ queue Để truy nhập vào queue ta phải dùng 2
biến trỏ: font trỏ đầu hàng đợi và rear trỏ cuối hàng đợi Một phần tử được lấy ra khỏi queue bằng cách tìm phần tử của mảng tại vị trí font và tăng font thêm 1 Một phần tử được thêm vào queue bằng cách lưu trữ nó tại vị trí rear của mảng, giả sử rear không vượt quá độ dài lớn nhất MaxSize của mảng, sau đó tăng rear thêm 1 Sau đây ta xét một vài cấu hình cụ thể của queue với MaxSize = 5:
Trang 10Procedure Get(Var FistOb :Td); {lấy ra khỏi hàng đợi }
Ứng dụng của hàng đợi điển hình là thuật toán tìm kiếm theo chiều rộng
Trên bàn cờ vua quốc tế N*N ( n≤ 50) trong đó có một số ô có mìn Từ một ô không có mìn cho trước con mã có thể đi đến một ô khác được hay không Nếu được hãy chỉ ra đường đi ngắn nhất
File dữ liệu:
- Dòng 1 là N (kích thước bàn cờ)
- Dòng thứ nhất trong số N dòng tiếp theo:
* đầu tiên là K số mìn trên dòng đó, tiếp theo là K số, mỗi số là chỉ số cột có mìn
+ A[i,j] = 0 nếu ô (i,j) có mìn
+ A[i,j] = 1 nếu ô (i,j) không có mìn và mã chưa đến
+ A[i,j]= k (k>1) nếu ô (i,j) là bước thứ k của con mã
Put(ô xp); {đưa vào hàng đợi toạ độ ô xuất phát}
Nhan { ô xp }: = 0; {khởi tạo nhãn của ô xuất phát}
Trang 11Bài 1 Chiến trường Ô qua – Nguồn bài: vn.spoj.com
Lại nói về Lục Vân Tiên, sau khi vượt qua vòng loại để trở thành Tráng Sỹ, anh đãgặp được Đôrêmon và được chú mèo máy cho đi quá giang về thế kỷ 19 Trở lại quêhương sau nhiều năm xa cách, với tấm bằng Tráng Sỹ hạng 1 do Liên Đoàn Type Thuậtcấp, anh đã được Đức Vua cử làm đại tướng thống lãnh 3 quân chống lại giặc Ô Qua xâmlăng
Đoàn quân của anh sẽ gồm N đại đội, đại đội i có A[i] (A[i] > 0) người Quân sỹtrong 1 đại đội sẽ đứng thành 1 cột từ người 1 -> người A[i] , như vậy binh sỹ sẽ đứngthành N cột Vì Vân Tiên quyết 1 trận sẽ đánh bại quân Ô Qua nên đã cử ra 1 quân đoànhùng mạnh nhất Trong sử cũ chép rằng, quân đoàn của Vân Tiên cử ra lúc đó là mộtnhóm các đại đội có chỉ số liên tiếp nhau (tức là đại đội i , i + 1 , … j) - Vì sử sách thìmối mọt hết cả nên chỉ biết được mỗi thế Ngoài ra theo giang hồ đồn đại thì sức mạnhcủa 1 quân đoàn = số người của đại đội ít người nhất * số đại đội được chọn
Nhiệm vụ của bạn là dựa trên các thông số của các nhà khảo cổ có được , hãy chobiết quân đoàn mà Vân Tiên đã chọn ra là từ đại đội nào đến đại đội nào Chú ý nếu cónhiều phương án thì ghi ra phương án mà chỉ số của đại đội đầu tiên được chọn là nhỏnhất
Trang 12Output
Kết quả mỗi test ghi ra trên 1 dòng, gồm 3 số: sức mạnh quân đoàn mạnh nhất, chỉ
số của đại đội đầu tiên và chỉ số của đại đội cuối cùng được chọn
Ví dụ:
24
3 4 3 14
1 2 1 3
9 1 3
4 1 4
Hướng dẫn thuật toán:
Xin tóm tắt lại đề bài như sau:
Trong tất cả các đoạn phần tử liên tiếp, hãy chọn ra đoạn [i … j] sao cho tích:min{A[i],…,A[j]} * (j – i + 1) đạt giá trị lớn nhất
- Với đặc điểm của bài toán, rõ ràng với phần tử A[i] ta sẽ phải tìm hai chỉ số j và k(trong đó A[j] phía trước A[i] và A[k] phía sau A[i]) sao cho A[j] gần với A[i] nhất vàA[j] < A[i], A[k] gần với A[i] nhất và A[k] < A[i] Từ đó cập nhật giá trị lớn nhất vơiA[i] * (k – j – 1)
- Để tìm A[j] ta có thể duyệt từ A[i] ngược về 1, để tìm A[k] ta sẽ duyệt từ A[i] tiếptục đến N, tuy nhiên cách này sẽ bị lỗi quá thời gian Ta có thể sử dụng STACK đểlàm giảm thời gian tìm kiếm A[j] và A[k]:
+ Gọi L[i] là chỉ số của phần tử A[L[i]] sao cho L[i] < i và L[i] gần với i nhất đểA[L[i]] <= A[i]
Trang 13- Nếu A[i] > A[STACK[top]] thì bổ sung i vào STACK và L[i] = STACK[top]
- Nếu A[i] <= A[STACK[top]] thì loại dần các phần tử ở đỉnh STACK cho đếnkhi A[i] > A[STACK[top]] Lúc đó gán L[i] = STACK[top]
+ Tương tự gọi R[i] là chỉ số của phần tử A[R[i]] sao cho i < R[i] và R[i] gần với inhất để A[R[i]] < A[i]
Cách tìm mảng R tương tự như với việc tìm mảng L nhưng theo chiều ngược lại
KQ cuối cùng = max{A[i] * (R[i] – L[i] + 1)}
Bài 2 KIỂM TRA TIN HỌC (PREVNOI 2013- Nguyễn Thế Hùng)
Dạy tin học cơ sở luôn là công việc vất vả ngay cả với những giáo viên nhiều kinhnghiệm như thầy HUNGNT Trong giờ bài tập tin học, có học sinh ngồi quanh một bàntròn, các học sinh được đánh số từ 1 tới theo chiều kim đồng hồ Xuất phát từ một vị trí
từ đầu buổi học, thầy HUNGNT phải đi một vòng quanh bàn theo chiều kim đồng hồ đểhướng dẫn từng bạn theo đúng thứ tự thầy đi qua Mỗi bạn được thầy hướng dẫn đúng micro giây (”s) và sau đó bắt tay vào lập trình ngay trong khi thầy chuyển sang hướngdẫn bạn kế tiếp theo chiều kim đồng hồ…, thời gian di chuyển của thầy coi như khôngđáng kể
Do biết rõ kỹ năng lập trình của từng bạn, thầy HUNGNT có thể ước lượng chính xácrằng bạn học sinh thứ sau khi được thầy hướng dẫn sẽ cần đúng 𝑎𝑖 ”s để viết xongchương trình của mình (∀𝑖 = 1,2,… , 𝑛) Vấn đề là thầy muốn kết thúc buổi học càngsớm càng tốt, muốn vậy, việc chọn học sinh nào hướng dẫn đầu tiên phải được tính toán
kỹ lưỡng…
Yêu cầu: Bạn được cho biết số , giá trị , dãy 𝐴 = (𝑎1, 𝑎2,… , 𝑎𝑛) Hãy giúp thầyHUNGNT chọn vị trí xuất phát sao cho thời gian từ lúc bắt đầu buổi học cho tới khi tất cảcác học sinh viết xong chương trình của mình là nhỏ nhất
Để tránh việc phải đọc một lượng dữ liệu quá lớn, dãy sẽ được cho bởi ba số nguyêndương 𝑝, 𝑞, 𝑚, trong đó mỗi phần tử được xác định theo công thức:
𝑎𝑖 = (𝑝 ∗ 𝑖) mod 𝑚 + 𝑞 (∀𝑖: 1 ≤ 𝑖 ≤ 𝑛)
Dữ liệu: Vào từ file văn bản PERIOD.INP
Dòng 1 chứa hai số nguyên dương 𝑛, Δ (𝑛 ≤ 5.106; Δ ≤ 109)
Trang 14 Dòng 2 chứa ba số nguyên dương 𝑝, 𝑞, 𝑚 xác định dãy (𝑝,
𝑞, 𝑚 ≤ 109) Các số trên một dòng của input file được ghi cách
nhau bởi dấu cách
Kết quả: Ghi ra file văn bản PERIOD.OUT một số nguyên duy nhất là thời gian (tính
bằng ”s) từ lúc bắt đầu buổi học cho tới khi tất cả các học sinh viết xong chương trìnhtheo phương án tìm được
Ví dụ
Giải thích: Δ = 3; Dãy 𝐴 = (3,5,7,9,2)
Phương án tối ưu: Thầy bắt đầu với học sinh 2,
Thời điểm viết xong chương trình của từng học sinh như sau:
40% số điểm ứng với các test có 𝑛 ≤ 103
30 % số điểm ứng với các test có
30% số điểm ứng với các test có 𝑛 ∈ [106,5.106]
Hướng dẫn giải thuật :
Xây dựng dãy 𝑏1,𝑏2,…,𝑏2𝑛−1 từ dãy như sáu:
Trang 15… 𝑏2𝑛−1 =𝑎𝑛−1 +( 𝑛−1)Δ
Có thể hiểu là 𝑏𝑖 được tính bằng phần tử thứ trong dãy theo vòng tròn cộng thêm 𝑖Δ
Dễ thấy rằng nếu HUNGNT bắt đầu từ học sinh 1 thì thời gian giờ học kéo dài là
max{𝑏[1…𝑛]} Nếu bắt đầu từ học sinh 2 thì thời gian giờ học kéo dài là
max{𝑏[2…𝑛+1]}−Δ… Tổng quát: nếu bắt đầu từ học sinh thì thời gian giờ học kéo dài
là
Từ đây có thể tóm tắt thuật toán như sáu: Với mỗi vị trí , ta cần nhánh chóng xác định𝑀[𝑖] là giá trị lớn nhất trong phần tử tính từ Khi đó đáp số là Việc xác định giá trị lớn nhất trong các đoạn gồm phần tử liên tiếp trong dãy có thể thực hiện trong thời gian Ο(𝑛) bằng cách sử dụng hàng đợi hái đầu chứa các chỉ số trong với các phép toán:
GetFront: Trả về phần tử đầu
GetRear: Trả về phần tử cuối
PopFront: Hủy phần tử đầu
PopRear: Hủy phần tử cuối
Push( ): Đẩy phần tử vào cuối hàng đợi
Bài 3: Optimal Programs
File chương trình OPTIMAL.PAS
Giới hạn thời gian 1 giây
Trang 16Như bạn đã biết, viết chương trình thường là việc không dễ dàng Mọi việc thậm trítrở nên khó khăn nếu chương trình của bạn cần được hoàn thành nhanh nhất có thể Vàđôi khi cũng có lý do để làm việc đó Rất nhiều chương trình lớn như hệ điều hành hoặc
cơ sở dữ liệu gặp phải sự “tắc nghẽn” - các đoạn mã được thực hiện đi và thực hiện lại, vàchiếm một phần lớn thời gian chạy ở đây người ta thường phải viết lại đoạn mã đó bằnghợp ngữ (assembly), từ đó thời gian chạy đạt được nhỏ nhất và sẽ rất quan trọng nếu đoạn
mã này được thực hiện hàng tỉ lần
Trong bài toán này, chúng ta xem xét nhiệm vụ tự động sinh ra mã hợp ngữ tối ưu.Cho trước một hàm số (như là một dãy các cặp vào/ra), bạn phải tạo ra một chương trìnhhợp ngữ ngắn nhất để tính hàm số này
Các chương trình bạn tạo ra sẽ phải chạy trên một stack cơ sở, nó chỉ hỗ trợ 5 câulệnh: ADD, SUB, MUL, DIV và DUP Bốn câu lệnh đầu lấy ra 2 phần tử trên đỉnh stack
và đẩy vào stack tương ứng tổng, hiệu, tích hoặc thương nguyên của phép chia (giốngphép toán div trong Pascal) của chúng Câu lệnh DUP đẩy thêm vào một phần tử giốngphần tử trên đỉnh stack Như vậy, nếu các câu lệnh được áp dụng trên một stack với 2
phần tử trên đỉnh là a và b thì kết quả của stack như sau:
Stacklúc đầu
ADD
SUB
MU
DUP
Có 3 trường hợp mà stack rơi vào trạng thái lỗi:
Câu lệnh DIV được thực hiện và phần tử trên đỉnh stack là 0
Các lệnh ADD, SUB, MUL hoặc DIV được thực hiện trong khi stack chỉ chứa 1 phầntử
Một phép tính sinh ra giá trị có giá trị tuyệt đối lớn hơn 30000
Dữ liệu: File vào bao gồm các mô tả một dãy các hàm số Mỗi mô tả bắt đầu với một
dòng chứa một số nguyên n (n 10), là số các cặp vào/ra tiếp theo Hai dòng tiếp theo: dòng thứ nhất chứa n số nguyên x 1 , x 2 , , x n (tất cả khác nhau) và dòng thứ hai chứa y 1,
y 2 , , y n Các số có giá trị tuyệt đối không vượt quá 30000
Kết thúc file vào bằng một trường hợp kiểm tra bắt đầu với n = 0 Trường hợp kiểm tra
này là không phải xử lý
Trang 17Kết quả: Bạn phải tìm chương trình ngắn nhất tính hàm f, sao cho f(x i ) = y i với mọi i
1, , n Điều này nghĩa là chương trình bạn đưa ra có thể không vào trạng thái lỗi nếu
thực hiện các dữ liệu vào x i (mặc dù nó có thể rơi vào trạng thái lỗi đối dữ liệu vào khác).Chỉ xem xét các chương trình có nhiều nhất 10 câu lệnh
Với mỗi một mô tả hàm, đầu tiên ghi ra số thứ tự của mô tả đó Sau đó ghi ra dãy các câulệnh làm nên chương trình ngắn nhất tính hàm cho trước này Nếu có nhiều hơn mộtchương trình như vậy, thì hãy đưa ra chương trình nhỏ nhất theo thứ tự sắp xếp từ điển.Nếu không có chương trình có tối đa 10 câu lệnh thì in ra dòng chữ “Impossible” Nếuchương trình ngắn nhất có không câu lệnh thì in ra “Empty Sequence”
Ghi một dòng trắng sau mỗi trường hợp kiểm tra
Ví dụ:
OPTIMAL.INP OPTIMAL.OUT4
1 2 3 4
0 -2 -6 -123
1 2 3
1 11 20031
200320030
Program 1DUP DUP MUL SUB
Program 2Impossible
Program 3Empty sequence
= ('ADD', 'DIV', 'DUP', 'MUL', 'SUB');
{ sap theo thu tu tu dien }
stack : array[1 max] of Sequence;
inp, out : text;
Function Read_data : integer;
Var i : integer;
Begin
d := d + 1;