II, Một số thủ tục và hàm cần dùng : -Khởi tạo procedure init; begin dst:=0; end; -Kiểm tra xem Stack ñã rỗng chưa Function StackEmpty:Boolean; Begin StackEmpty:= dst=0; End; -ðưa phần t
Trang 11
2
n
Cấu trúc ngăn xếp và hàng ñợi
*****
Nguyễn ðức Huy Tin Lê Khiết 2006-2009
A - Cấu trúc ngăn xếp (stack) :
I, Khái niệm :
1, ðịnh nghĩa :
-ðây là kiểu dữ liệu mà việc cập nhật và truy nhập nó ñều theo nguyên tắc "Vào sau ra trước"( Last In, Firt Out)
2, Khai báo :
Var stack:array[1 1000] of byte;
Dst:integer;
Ý nghĩa : phần tử ñưa vào cuối cùng sẽ ñược lấy ra ñầu tiên
II, Một số thủ tục và hàm cần dùng :
-Khởi tạo
procedure init;
begin
dst:=0;
end;
-Kiểm tra xem Stack ñã rỗng chưa
Function StackEmpty:Boolean;
Begin
StackEmpty:= (dst=0);
End;
-ðưa phần tử mới vào
procedure push(x:byte);
begin
inc(dst);
stack[dst]:=x;
end;
-Lấy phần tử trên cùng ra
function pop:byte;
begin
pop:=stack[dst];
dec(dst);
end;
-ðọc phần tử trên cùng của ngăn xếp
function get:byte;
begin
get:=stack[dst];
end;
III, Ví dụ :
Trang 21 Dùng ngăn xếp ñể duyệt sâu
Procedure DFS;
Var x,i:integer;
Begin
Push(1);
Repeat
Pop(x);
For i:=n downto 1 do
If (a[x,i]=1)and(kt[i]=false) then
Begin
Push(i);
Kt[i]:=true;
End;
Until dst=0;
End;
2 ðường ñi ngắn nhất
Cho một lưới ô vuông m x n (1<m,n<=250) gồm các số 0,1 Từ 1 ô số 0 có thể ñi sang các ô 0 chung cạnh và không ñược qua các ô 1 Một người ở 1 ô số 0 bất kì, tìm ñường ngắn nhất ñể người ñó ra ngoài lưới ô vuông, biết ñộ dài ñường ñi là số ô vuông nó chiếm
Bài này mà dùng ñệ quy duyệt thì không tối ưu, ta có thể dùng 2 ngăn xếp chứa tọa ñộ thay vì ñệ quy, dữ liệu xử lí sẽ lớn hơn rất nhiều
Chương trình :
const u:array[1 4] of shortint=(0,0,-1,1);
v:array[1 4] of shortint=(1,-1,0,0);
var a:array[1 250,1 250] of shortint;
m,n,s,t:byte;
stx,sty:array[1 500] of integer; { -hai ngăn xếp -}
dst:integer;
{ -}
function inside(x,y:byte):boolean;
begin
inside:=true;
if (x<1)or(y<1)or(x>m)or(y>n) then inside:=false;
end;
{ -}
function fmin(x,y:byte):shortint;
var i:integer;
min:shortint;
begin
min:=125;
for i:=1 to 4 do
if inside(x+u[i],y+v[i]) then
if (a[x+u[i],y+v[i]]<min)and(a[x+u[i],y+v[i]]>0) then min:=a[x+u[i],y+v[i]];
fmin:=min;
Trang 3end;
{ -}
procedure nhap;
var f:text;
i,j:integer;
c:char;
begin
assign(f,'path1.inp');
reset(f);
readln(f,m,n);
readln(f,s,t);
for i:=1 to n do
begin
for j:=1 to n do
begin
read(f,c);
if c='1' then a[i,j]:=-1;
end;
readln(f);
end;
close(f);
end;
{ -Lưu vào stack -}
procedure savst(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;
{ -Lấy ra từ stack -}
procedure popst(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;
{ -}
procedure duyet;
var x,y,i,j:integer;
begin
savst(s,t);
a[s,t]:=1;
repeat
popst(x,y);
Trang 4for i:=1 to 4 do
if inside(x+u[i],y+v[i]) then
if a[x+u[i],y+v[i]]=0 then
begin
savst(x+u[i],y+v[i]);
a[x+u[i],y+v[i]]:=fmin(x+u[i],y+v[i])+1;
end;
until dst=0;
end;
{ -}
procedure xuat;
var i,min:integer;
f:text;
begin
assign(f,'path.out');
rewrite(f);
min:=maxint;
for i:=1 to m do
begin
if (a[i,1]<min)and(a[i,1]>0) then min:=a[i,1];
if (a[i,n]<min)and(a[i,n]>0) then min:=a[i,n];
end;
for i:=1 to n do
begin
if (a[1,i]<min)and(a[1,i]>0) then min:=a[1,i];
if (a[m,i]<min)and(a[m,i]>0) then min:=a[m,i];
end;
if min=maxint then write(f,-1)
else write(f,min);
close(f);
end;
{ -}
procedure work;
begin
if a[s,t]=-1 then
begin
writeln('Test sai');
readln;
halt;
end;
duyet;
end;
{ -}
BEGIN
Trang 5nhap;
work;
xuat;
END
3 Tìm chu trình Euler trong một ñồ thị vô hướng bằng cách sử dụng Stack
Chương trình :
Var a:array[1 100,1 100] of integer;
N:byte;
Procedure nhap;
Begin
{ }
end;
function kiemtra:boolean;
var i,j:integer;
begin
kiemtra:=false;
for i:=1 to n do
begin
s:=0;
for j:=1 to n do
s:=s+a[i,j];
if s mod 2 =1 then exit;
end;
kiemtra:=true;
end;
{ -}
procedure euler;
var ce,stack:array[1 500] of byte;
d,top:byte;
begin
top:=1;
stack[top]:=1;
repeat
v:=stack[top];
x:=1;
while (x<=n)and(a[v,x]=0) do
inc(x);
if x>n then
begin
inc(d);
ce[d]:=x;
dec(top);
end
else
Trang 6begin
inc(top);
stack[top]:=x;
a[v,x]:=0;
a[x,v]:=0;
end;
until top=0;
for x:=1 to d do
write(ce[x],’ ‘);
end;
{ -}
BEGIN
Nhap;
If kiemtra then euler
Else writeln(‘Khong co duong.’);
Readln;
End
4 Tô màu một hình kín
Chương trình :
Var a:array[1 250,1 250] of byte;
Stx,sty:array[1 1000] of byte;
Dst:integer;
x0,y0:byte;
{ -}
procedure push(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;
{ -}
procedure pop(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;
{ -}
procedure fill(color:byte);
var x,y:integer;
begin
dst:=0;
push(x0,y0);
repeat
Trang 7pop(x,y);
if (inside(x,y))and(a[x,y]<>color) then
begin
a[x,y]:=color;
if x<n then push(x+1,y);
if x>1 then push(x-1,y);
if y<n then push(x,y+1);
if y>1 then push(x,y-1);
end;
until dst=0;
end;
5 Cho ma trận 0,1 Có thể ñi qua các ô 0, cho tọa ñộ 2 ô 0, kiểm tra xem chúng có liên thông không
Chương trình :
Var a:array[1 100,1 100] of byte;
n:byte;
dst:integer;
x1,x2,y1,y2:byte;
stx,sty:array[1 1000] of byte;
{ -}
procedure push(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;
{ -}
procedure pop(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;
{ -}
function kiemtra:boolean;
var x,y,i,j:byte;
begin
push(x1,y1);
repeat
pop(i,j); x:=i; y:=j;
{ -}
while (a[x,y-1]=0)and(y-1>0) do
begin
a[x,y]:=1; dec(y);
Trang 8end; push(x,y); y:=j
{ -}
while (a[x,y+1]=0)and(y<n) do
begin
a[x,y]:=1; inc(y);
end; push(x,y); y:=j;
{ -}
while (a[x-1,y]=0)and(x-1>0) do
begin
a[x,y]:=1; dec(y);
end; push(x,y); x:=i;
{ -}
while (a[x+1,y]=0)and(x<0) do
begin
a[x,y]:=1; inc(y);
end; push(x,y);
{ -}
until (dst=0)or((stx[dst]=x2)and(sty[dst]=y2));
if dst=0 then kiemtra:=false
else kiemtra:=true;
end;
6 Khử ñệ quy thuật toán sắp xếp Quicksort
Chương trình :
{=======================}
Const Nmax=5000;
Var n, i, j, x, l, r, tg:Integer;
s:0 Nmax;
A: Array[1 Nmax] of Integer;
Stack: Array [1 Nmax] of Record 1 , r : Integer; End;
Procedure Sort;
Begin
S:=1;
Stack [s] 1:=1;
Stack [s] r :=n ;
Repeat
1:=Stack[s] 1 ;
r: = Stack [s] r;
Dec(s);
Repeat
x:=A[ (1+r)div 2];
While a[i] < x Do Inc(i);
Trang 9While a [j] > x Do Dec (j);
Until i >j;
If i < r then
Stack [s] 1 : = 1;
Until 1> r ;
Until S= 0;
End;
7 Thuật toán tô màu ñồ thị
Bài tập áp dụng :
1 Cho bảng vuông n x n Tại ô (i,j) là ô ñen, còn lại ñều là ô trắng Tìm cách ñặt sau cho mỗi dòng, mỗi cột có ñúng 1 ô ñen
B - Cấu trúc hàng ñợi (queue) :
B1 – Hàng ñợi bình thường :
I, Khái niệm:
1, ðịnh nghĩa :
-Khác với Stack, Queue là một kiểu dữ liệu trừu tượng mà cơ chế cập nhật và truy xuất xảy
ra ở hai ñầu khác nhau và theo quy tắc vào trước ra trước ( First In - First Out)
2, Khai báo :
Var queue:array[1 1000] of byte;
Last, first:integer;
Ý nghĩa : với cấu trúc này, phần tử ñưa vào trước ñược lấy ra trước
* Khuyết ñiểm :ðối với hàng ñợi thường khi thêm vào ở Last và loại bỏ ở First, phần sử dụng của hàng có khuynh hướng di chuyển về phía dưới, và ñến một lúc nào ñó thì ta không thể thêm phần tử mới vào hàng nữa (vì số phần tử tối ña của hàng là cố ñịnh) Khi
ñó ta nói rằng hàng bị tràn
II, Một số thủ tục và hàm cần dùng :
-Khởi tạo
Procedure Init;
Begin
First:=1; Last:=0;
End;
Trang 101
2
n
-Kiểm tra xem hàng ñợi ñã rỗng chưa
Function QueueEmpty : Boolean;
Begin
QueueEmpty : =First>last;
End;
-ðưa phần tử mới vào
procedure push(x:byte);
begin
inc(last);
queue[last]:=x;
end;
-Lấy phần tử dưới cùng ra
function pop:byte;
begin
If not QueueEmpty then
begin
pop:=queue[first];
inc(first);
end;
end;
B2 – Hàng ñợi vòng :
I, Khai báo :
Var queue:array[1 max] of byte;
Last, first:integer;
Hàng ñợi vòng là cải tiến của hàng ñợi bình thường ñể sử dụng tối ña mảng hàng ñợi
II, Các thủ tục ñược cải tiến :
-ðưa phần tử mới vào
procedure push(x:byte);
begin
last:=(last+1) mod max; { Ta cho vị trí của n quay 1 vòng }
queue[last]:=x; { giống như 1 cây kim ñồng hồ }
end; { sau 1 vòng trở về chỗ cũ, ñi hết n vị trí }
-Lấy phần tử dưới cùng ra
function pop:byte;
begin
If not QueueEmpty then
begin
pop:=queue[first];
first:=(first+1) mod max;
end;
end;
B3, Bài tập ví dụ :
1 Dùng hàng ñợi ñể duyệt rộng trên ñồ thị
N=1 N-1
Trang 11var a:array[1 250,1 250] of byte;
kt:array[1 250] of boolean;
Procedure BFS;
Var x,i:integer;
Begin
Push(1);
Repeat
Pop(x);
For i:=1 to n do
If (a[x,i]=1)and(kt[i]=false) then
Begin
Push(i);
Kt[i]:=true;
End;
Until first>last;
End;
2 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ứ i 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
- Dòng cuối ghi 4 số x1, y1,x2, y2:
* (x1,y1): toạ ñộ ô xuất phát
* (x2,y2) : Toạ ñộ ô ñích
Chương trình :
const fi='baimin.inp';
u:array[1 8] of -2 2 =(1,2,2,1,-1,-2,-2,-1);
v:array[1 8] of -2 2 =(2,1,-1,-2,-2,-1,1,2);
type oo=record
x,y:shortint;
end;
var a:array[-1 50,-1 50] of oo; { ô a[i,j] =(0,0) nếu không có mìn và
n,kq:byte; chưa ñược ñi qua, (x,y) nếu từ ô (x,y)
q:array[1 2500] of oo; ngựa nhảy tới ô (i,j) và (100,100)
first,last:integer; nếu có mìn }
xp,fn:oo;
procedure nhap;
var f:text;
tg:oo;
i,k,j,t:integer;
begin
Trang 12kq:=0;
tg.x:=100;
tg.y:=100;
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,k);
for j:=1 to k do
begin
read(f,t);
a[i,t]:=tg;
end;
end;
read(f,xp.x,xp.y);
a[xp.x,xp.y]:=tg;
read(f,fn.x,fn.y);
close(f);
last:=0;
first:=1;
end;
function empty:boolean;
begin
empty:= first>last;
end;
procedure push(m:oo);
begin
last:=(last+1)mod 2500;
q[last]:=m;
end;
procedure pop(var x:oo);
begin
if not empty then
begin
x:=q[first];
first:=(first+1) mod 2500;
end;
end;
function inside(o:oo):boolean;
begin
inside:=(1<=o.x)and(o.x<=n)and(1<=o.y)and(o.y<=n);
end;
function ok(o:oo):boolean;
x
x
x
x
x
x
ð
ð
S
F
(1,1)->(2,3)->(3,1)->(4,3)
Trang 13begin
ok:=(a[o.x,o.y].x=0)and(a[o.x,o.y].y=0)and(inside(o));
end;
procedure xuat;
var o:oo;
begin
o:=fn;
kq:=1;
while (o.x<>xp.x)or(o.y<>xp.y) do
begin
write('(',o.x,',',o.y,') <-');
o:=a[o.x,o.y];
end;
write('(',o.x,',',o.y,')');
end;
procedure main;
var o,otg:oo;
i:integer;
b,dc:byte;
procedure restore;
begin
first:=first-1;
if first=0 then first:=2500;
a[q[first].x,q[first].y].x:=0;
a[q[first].x,q[first].y].y:=0;
first:=(first+1) mod 2500;
end;
begin
push(xp);
repeat
pop(o);
dc:=0;
inc(b);
for i:=1 to 8 do
begin
otg.x:=o.x+u[i];
otg.y:=o.y+v[i];
if ok(otg) then
begin
a[otg.x,otg.y]:=o;
if(otg.x=fn.x)and(otg.y=fn.y) then
begin
xuat;
break;
Trang 14end;
dc:=1;
push(otg);
end;
end;
if(i=8)and(dc=0) then
restore;
until empty;
if kq=0 then writeln('Can not');
end;
Begin
nhap;
main;
readln;
end
3 Bài toán ñong nước :
Cho 2 bình dung tích lần lượt là m và n ( lít) Với nguồn nước không hạn chế, dùng 2 bình trên ñể ñong k lít nước với k<min(m,n)
Ví dụ : m = 5, n = 4, k =3
(0,0) -> (0,4) -> (4,0) -> ( 4,4) -> (5,3)
Chương trình :
Program donuoc;
type
trangthai = record
x,y,tt,truoc: integer;
end;
maxQ=0 1000;
mangTT=array[maxQ] of trangthai;
var
n, m, k: byte;
MO,DONG,Stack: mangtt;
topM,bottomM,topD,bottomD,top:maxQ;
xp:trangthai;
procedure sPush(var s:mangTT;var top:maxQ;u:trangthai);
begin
top:=top+1;
s[top]:=u;
end;
procedure sPop(s:mangtt;var top:maxQ;var u:trangthai);
begin
u:=s[top];
top:=top-1;
Trang 15end;
procedure qPush(var Q:mangTT;var top,bottom:maxQ;u:trangthai);
begin
top:=top+1;
Q[top]:=u;
if bottom=0 then bottom:=1;
end;
procedure qPop(Q:mangtt;var top,bottom:maxQ;var u:trangthai);
begin
u:=Q[bottom];
if bottom=top then begin bottom:=0;top:=0;end
else bottom:=bottom+1;
end;
function ktThuocHD(x,y:byte;top,bottom:maxq;Q:mangtt):boolean;
var i:maxQ;
begin
if bottom=0 then ktthuocHD:=false
else
begin
i:=bottom;
While (i<=top) and ((q[i].x<>x) or (q[i].y<>y )) do
i:=i+1;
ktthuochd:=(i<=top);
end;
end;
procedure ChuyenTT(u:trangthai;var v:trangthai;i:byte);
begin
case i of
1: begin v.x:=n; v.y:=u.y;end;
2: begin v.x:=u.x; v.y:=m; end;
3: begin v.x:=0; v.y:=u.y;end;
4: begin v.x:=u.x; v.y:=0;end;
5: if (u.x+u.y <= m) then begin v.x:=0; v.y:=u.x+u.y;end
else begin v.x:= u.x+u.y-m; v.y:=m;end;
6: if u.x+u.y <= n then begin v.x:=u.x+u.y; v.y:=0; end
else begin v.y:=u.x+u.y-n; v.x:=n;end
end;
end;
Procedure inkq;
var i,j:maxq;u:trangthai;
begin
Trang 16i:=topd;
while dong[i].tt<>0 do
begin
j:=dong[i].truoc;
spush(mo,top,dong[i]);
while dong[i].tt<>j do i:=i-1;
end;
{ spush(mo,top,dong[i]);}
while Top<>1 do
begin
spop(mo,top,u);
write('(',u.x,',',u.y,') >');
end;
spop(mo,top,u);
writeln('(',u.x,',',u.y,')');
end;
procedure bfs(u: trangthai);
var
n,m:trangthai;
tt:maxq;i:byte;
begin
qPush(MO,topm,bottomM,u);
tt:=1;
while bottomM<>0 do
begin
qPop(Mo,topm,bottomM,n);
qPush(Dong,topd,bottomd,n);
for i:=1 to 6 do
begin
chuyenTT(n,m,i);
if (not ktthuochd(m.x,m.y,topm,bottomM,Mo) ) and
(not ktthuochd(m.x,m.y,topd,bottomd,dong) ) then
begin
tt:=tt+1;
m.tt:=tt;
m.truoc:=n.tt;
qPush(Mo,topm,bottomM,m);
if (m.x=k) or (m.y=k) then
begin qpush(dong,topd,bottomd,m);inkq; exit; end;
end;
end;
end;
writeln('Khong thanh cong');
Trang 17end;
procedure KhoiTao;
begin
write('Nhap n, m va k:');
readln(n,m,k);
xp.x:=0;xp.y:=0;xp.tt:=1;xp.truoc:=0;
topm:=0;bottomm:=0;topd:=0;bottomd:=0;
top:=0
end;
BEGIN
khoitao;
bfs(xp);
readln;
END
Bài tập áp dụng :
1 Bài toán mã ñi tuần : Cho bàn cờ n x n Con mã ñứng tại ô (x0,y0), hãy tìm một ñường
ñi ñể con mã ñi qua tất cả các ô bàn cờ, mỗi ô một lần