Bây giờ ta đưa ra thứ tự duyệt các đỉnh của đồ thị đã cho theo thuật toán tìm kiếm theo chiều rộng; thứ tự duyệt có thể bắt đầu từ một đỉnh v nào đó.. + Đối với dữ liệu vào mà quan hệ gi
Trang 1THUẬT TOÁN LOANG
-I) ĐẶT VẤN ĐỀ:
Thuật toán Loang thực chất là thuật toán tìm kiếm theo chiều rộng trên đồ thị (Breadth First
Search) Để hiểu rõ bản chất của thuật toán này, ta xét bài toán ‘Thăm các đỉnh của một đồ thị’ như sau: Cho một đồ thị vô hướng G = (V,E), N đỉnh và M cạnh (số hiệu của các đỉnh là 1,2,
…,N) Bây giờ ta đưa ra thứ tự duyệt các đỉnh của đồ thị đã cho theo thuật toán tìm kiếm theo chiều rộng; thứ tự duyệt có thể bắt đầu từ một đỉnh v nào đó Tư tưởng của thuật toán là sử dụng cấu trúc dữ liệu kiểu hàng đợi (QUEUE - vào trước ra trước) Phần tử được nạp vào đầu tiên của QUEUE là đỉnh v Sau đó cứ mỗi đỉnh p lấy ra khỏi QUEUE là ta thăm đỉnh đó đồng thời nạp vào QUEUE những đỉnh chung cạnh với p (chỉ nạp vào những đỉnh chưa xét đến) Quá trình trên được lặp đi lặp lại cho đến khi nào QUEUE rỗng thì dừng
QUEUE = ; {Khởi tạo QUEUE ban đầu là rỗng}
QUEUE <= v; {Nạp đỉnh v vào QUEUE}
Chuaxet[v]:=False;{Đỉnh v nạp vào QUEUE là đã xét rồi => cờ của v là False}
While QUEUE ≠ do
Begin
P <= QUEUE; {Lấy p từ QUEUE}
Thăm đỉnh p;
For u € Ke(p) do {Những đỉnh u chung cạnh với đỉnh p}
If Chuaxet(u) then {Nếu đỉnh u chưa xét đến}
Begin
QUEUE <= u; {Nạp u vào QUEUE}
Chuaxet[u]:=False; {Đỉnh u đã xét rồi =>cờ của u là False }
Người ta thường dùng dữ liệu kiểu mảng để biểu diễn cấu trúc dữ liệu kiểu hàng đợi QUEUE
và sử dụng 2 biến Dau và Cuoi để điều khiển việc nạp vào và lấy phần tử ra (biến Dau điều khiển thao tác lấy ra, biến Cuoi điều khiển thao tác nạp vào)
Với bài toán trên ta sử dụng mảng 1 chiều Q: Array[1 N] of Byte để biểu diễn QUEUE Khi
đó thao tác nạp vào và lấy ra được thực hiện như sau:
FillChar(Q,SizeOf(Q),0); {Khởi tạo tất cả các phần tử của Q có giá trị 0}
Dau:=1;
Cuoi:=1;
Q[cuoi]:=v; {Ban đầu nạp đỉnh v vào Q}
Để nạp thêm đỉnh u nào đó vào Q ta thực hiện:
Cuoi:=Cuoi+1; {Hoặc dùng lệnh Inc(Cuoi)}
Trang 2Để lấy một đỉnh p nào đó ra khỏi Q ta thực hiện:
P:=Q[Dau];
Inc(Dau);
Lưu ý: Ta nói lấy đỉnh p ra khỏi hàng đợi Q là lấy ra theo cơ chế điều khiển ( vì biến Dau đã
tăng lên một đơn vị qua lệnh Inc(Dau)); về mặt vật lý thì p vẫn đang nằm trong mảng Q Như vậy ta phải hiểu rằng các phần tử trong cấu trúc hàng đợi Q là các phần tử Q[Dau], ,Q[Cuoi]
A:Array[1 Nmax,1 Nmax] of 0 1;{Nếu có cạnh giữa đỉnh i và đỉnh j thì A[i,j]=1, ngược lại A[i,j]=0 }
Chuaxet:Array[1 Nmax] of Boolean;{Cờ của các đỉnh, có trạng thái True nếu chưa xét, ngược lại False} Q:Array[1 Nmax] of Byte; {Biểu diễn hàng đợi QUEUE}
Trang 3dau:=dau+1; {Lấy đỉnh p ra khỏi Q }
If (dau-1) mod 14 = 0 then Writeln(p:4) {In ra số hiệu đỉnh p - thao tác thăm đỉnh p}
Else Write(p:4); {trên màn hình xuất hiện mỗi dòng không quá 14 số}
Chương trình trên thực hiện với dữ liệu vào là tệp TKR_DT.INP có cấu trúc:
- Dòng đầu tiên, được gọi là dòng 0: ghi các số nguyên dương N, x cách nhau ít nhất là một ký tự trống (N: Số đỉnh của đồ thị; x: Đỉnh xuất phát);
- Trong các dòng tiếp theo: Dòng thứ i (i = 1 N-1) ghi N-i số 0 và 1 liên tiếp nhau cho biết giữa đỉnh i và đỉnh j có cạnh nối với nhau hay không (j = i + 1 N) Nếu số ghi ở vị trí j-i tính từ trái sang phải trên dòng thứ i có giá trị 1 thì có cạnh nối giữa đỉnh i và đỉnh j, nếu là giá trị 0 thì không có cạnh nối
Ví dụ với tệp TKR_DT.INP sau đây:
13 1101000000010010000000000001000000011000000101100001000001000000
Trang 410000 0000 110 10 0 Với tệp dữ liệu vào như trên ta phải hiểu như sau:
+ Ở dòng 0: N=13, x=1;
+ Ở dòng 1: 101000000010
- giữa đỉnh 1 với đỉnh 2 có cạnh nối với nhau
- giữa đỉnh 1 với đỉnh 3 không có cạnh nối với nhau
- giữa đỉnh 1 với đỉnh 4 có cạnh nối với nhau
- giữa đỉnh 1 với đỉnh 5 không có cạnh nối với nhau
- giữa đỉnh 1 với đỉnh 11 không có cạnh nối với nhau - giữa đỉnh 1 với đỉnh 12 có cạnh nối với nhau - giữa đỉnh 1 với đỉnh 13 không có cạnh nối với nhau + Ở dòng 2: 01000000000 - giữa đỉnh 2 với đỉnh 3 không có cạnh nối với nhau - giữa đỉnh 2 với đỉnh 4 có cạnh nối với nhau
+ Ở dòng 11: 10 - giữa đỉnh 11 với đỉnh 12 có cạnh nối với nhau - giữa đỉnh 11 với đỉnh 13 không có cạnh nối với nhau + Ở dòng 12: 0 - giữa đỉnh 12 với đỉnh 13 không có cạnh nối với nhau Quan hệ giữa các đỉnh được mô tả qua đồ thị sau:
2 3 5
7 8
4
1 6 9
12 13
10 11
Thứ tự các đỉnh được nạp vào hàng đợi Q và lấy ra tuần tự như sau:
Các phần tử
nạp vào Q
Các phần tử
có trạng thái
cờ là False
Các phần tử trong Q
Phần tử lấy
ra khỏi Q
Không nạp phần tử nào
khi lấy 2 ra khỏi Q
Trang 5khi lấy 10 ra khỏi Q
+ Với những bộ test dữ liệu bé thì ta có thể tạo một cách thủ công Nhưng với những bộ test
dữ liệu lớn thì ta phải tạo bằng chương trình Sau đây xin giới thiệu chương trình tạo bộ test với số lượng các đỉnh của đồ thị là 253:
Trang 6+ Đối với dữ liệu vào mà quan hệ giữa các đỉnh có dạng là một ma trận đầy đủ (ma trận đối xứng) và các số ghi trên mỗi dòng cách nhau ít nhất là một ký tự trống thì ta thay thủ tục đọc tệp bởi thủ tục sau đây:
Bạn đọc tự viết thủ tục đọc tệp trong trường hợp dữ liệu vào mà quan hệ giữa các đỉnh có dạng
là nữa trên hoặc nữa dưới của một ma trận đầy đủ và các số ghi trên mỗi dòng cách nhau ít nhất là một ký tự trống hoặc liền nhau
II) CÁC BÀI TOÁN ÁP DỤNG:
Bài 1: Tìm đường đi qua ít đỉnh nhất
Cho đồ thị vô hướng G = (V,E) có N đỉnh và M cạnh (các đỉnh có số hiệu là 1,2, ,N) Mối quan hệ giữa các đỉnh được cho bởi ma trận kề A[i,j]: nếu đỉnh i và đỉnh j có chung cạnh thì A[i,j] = 1, ngược lại A[i,j] = 0
Yêu cầu:
1) Tìm đường đi qua ít đỉnh nhất giữa 2 đỉnh bất kỳ nào đó của đồ thị
2) Tìm số lượng các thành phần liên thông của đồ thị (các đỉnh thuộc một vùng liên thông nếu luôn tồn tại đường đi giữa 2 đỉnh bất kỳ trong các đỉnh đó)
Trang 7Bàn về dung lượng bộ nhớ: Chương trình trên thực hiện với giá trị tối đa là 253 đỉnh của đồ
thị Vậy với đồ thị có số đỉnh lớn hơn 253 thì ta giải quyết thế nào?
Bài toán sau đây đưa ra một giải pháp để xử lý:
Bài 2: Đường đi trong đồ thị có nhiều đỉnh
Cho một đồ thị vô hướng có N đỉnh (N<=500000), các đỉnh có số hiệu là 1 N Hãy tìm đường
đi qua ít đỉnh nhất giữa 2 đỉnh x và y nào đó cho trước của đồ thị
Dữ liệu vào là tệp văn bản dothi.inp có cấu trúc:
- Dòng đầu tiên ghi các số N, x, y;
- Các dòng tiếp theo, mỗi dòng ghi 2 số là số hiệu của 2 đỉnh có chung cạnh;
- Các số ghi trên mỗi dòng cách nhau ít nhất là một ký tự trống
Dữ liệu ra là tệp văn bản dothi.out ghi số hiệu các đỉnh trong đường đi tìm được giữa 2 đỉnh x
và y, mỗi số ghi trên 1 dòng Nếu không tồn tại đường đi thì ghi thông báo ‘Khong ton tại duong di’
Phân tích:
Trang 8Nhìn dạng bài toán, chúng ta nhận thấy ngay thuật toán tối ưu để xử lý là thuật toán Loang Nhưng vì số đỉnh của đồ thị nằm trong phạm vi quá lớn (N<=500000) nên ta không thể sử dụng kiểu dữ liệu mảng để lưu trữ thông tin về các đỉnh và biểu diễn cấu trúc hàng đợi Vậy làm thế nào?
Tôi xin đưa ra một giải pháp: Lưu trữ các thông tin trong quá trình xử lý cũng như biểu diễn
cấu trúc hàng đợi để ‘Loang’ là bởi các tệp văn bản (vì chúng ta đã biết tệp là kho chứa dữ
liệu dường như vô tận).
Cụ thể như sau:
- Sử dụng hệ thống tệp văn bản *.ke để lưu các đỉnh chung cạnh với đỉnh có số hiệu
là giá trị số của phần đầu tên tệp: Ví dụ tệp 2.ke sẽ lưu trữ số 1 và số 4, mỗi số ghi trên một dòng
- Sử dụng hệ thống các tệp văn bản *.dd chứa số 1 hoặc 0 cho biết đỉnh có số hiệu là giá trị số của phần đầu tên tệp đã bị đánh dấu hay chưa; giá trị 1: đỉnh đó đã xét rồi, ngược lại là chưa xét Ví dụ tệp 5.dd chứa số 1 có nghĩa là đỉnh 5 đã được xét rồi trong quá trình ‘Loang’ (đã được nạp vào hàng đợi).Hệ thống tệp này khởi tạo ban đầu là chứa số 0
- Sử dụng hệ thống tệp văn bản *.tr chứa một số nguyên là số hiệu của đỉnh trước đỉnh có số hiệu là giá trị số của phần đầu tên tệp khi nạp vào hàng đợi Ví dụ tệp 6.tr chứa số 4 có nghĩa là đỉnh 6 được nạp vào hàng đợi nhờ đỉnh 4
- Sử dụng hệ thống các tệp *.que để biểu diễn cấu trúc hàng đợi Ví dụ dau = 1 và cuoi = 4 thì đỉnh đầu tiên của hàng đợi là đỉnh có số hiệu là số được chứa trong tệp 1.que Tương tự, với đỉnh thứ 2, thứ 3, thứ 4, của hàng đợi
- Sử dụng hệ thống các tệp văn bản *.kq để lưu các đỉnh trong quá trình lấy kết quả
Hệ thống các tệp văn bản trên được đưa ra ở phần khai báo hằng:
Trang 9Để nạp đỉnh v vào hàng đợi ta thực hiện:
Trang 11Readln(f1,ok); {Kiểm tra xem v đã bị đánh dấu hay chưa}
Close(f1);
If ok=0 then Begin Assign(f1,s2+dd);
Str(dem,s1);
Assign(f1,s1+kq);
Rewrite(f1);
Writeln(f1,i);
Trang 12+ Ta có thể tham khảo chương trình sau đây để tạo các bộ test dữ liệu lớn:
Trang 13If (yd=0) or (yd=xn) then
While (yd=0) or (yd=xn) do yd:=Random(MN+1);
Writeln(‘Hai đinh can tim duong di la:’,xn,’ ‘,yd);
Until Readkey = #27; {Khi nào thấy đạt yêu cầu rồi thì ấn phím ESC để dừng}
Trạng thái 1 Trạng thái 0
Hình 1
Trang 14tự trong xâu S thể hiện việc quả cầu vào lối vào với tên ký tự đó
Ví dụ: S=AABC có nghĩa là ta lần lượt thả quả cầu vào các lối A, A, B, C
Bài toán đặt ra như sau: Cho hai trạng thái bất kỳ T1, T2 của Otomat Hãy tìm một ký tự S ngắn nhất có thể được thể hiện hoạt động của Otomat chuyển từ trạng thái T1 đến trạng thái T2
Dữ liệu vào là tệp otomat.inp cócấu trúc:
- Dòng thứ nhất ghi 8 số 0 và 1 biểu diễn trạng thái T1;
- Dòng thứ hai ghi 8 số 0 và 1 biểu diễn trạng thái T2;
Các số trên mỗi dòng được ghi liên tiếp nhau
Dữ liệu ra là tệp otomat.out ghi xâu ký tự S tìm được Nếu không tìm được xâu S thì ghi ký tự
Trang 15If T[4]='0' then Begin T[4]:='1';
If T[6]='0' then T[6]:='1' else T[6]:='0';
End else {T[4]='1'}
Begin T[4]:='0';
If T[7]='0' then T[7]:='1' else T[7]:='0';
End;
Trang 16If T[6]='0' then T[6]:='1' Else T[6]:='0';
End Else {T[4]='1'}
If T[5]='0' then Begin T[5]:='1';
If T[7]='0' then T[7]:='1' else T[7]:='0';
End Else {T[5]='1'}
Begin T[5]:='0';
If T[8]='0' then T[8]:='1' else T[8]:='0';
If T[7]='0' then T[7]:='1' else T[7]:='0'
End Else {T[5]='1'}
Trang 17Begin T[5]:='0';
If T[8]='0' then T[8]:='1' else T[8]:='0';
End;
End Else {T[3]='1'}
Begin T[3]:='0';
If T[8]='0' then T[8]:='1' else T[8]:='0';
If i=1 then O:=Tha_A(P);
If i=2 then O:=Tha_B(P);
If i=3 then O:=Tha_C(P);
If i=1 then S[cuoi]:='A';
If i=2 then S[cuoi]:='B';
If i=3 then S[cuoi]:='C';
Trang 18Bài 5: Máy đổi thẻ tự động
Có một máy giải trí tự động có M cửa dùng để đổi thẻ Có các thẻ mã số từ 1, ,N Nếu ta bỏ thẻ có mã số i vào một cửa nào đó thì máy sẽ thu thẻ đó và cho ra một thẻ có mã số nào đó trong khoảng 1 N Máy đổi thẻ hoạt động theo thông tin ghi trong tệp văn bản the.inp :
- Dòng đầu tiên ghi các số N, M;
- N dòng tiếp theo, mỗi dòng chứa M số tạo thành một bảng có kích thước N x M Phần tử nằm trên dòng i, cột j của bảng này cho biết nếu ta bỏ thẻ có số hiệu i vào cửa j thì sẽ thu được thẻ có số hiệu chính là giá trị của phần tử đó
Các số trên mỗi dòng của tệp ghi cách nhau ít nhất là một ký tự trống
Yêu cầu:
Với mỗi cặp thẻ có số hiệu x và y cho trước (x<>y), hãy cho biết có cách nào nhanh nhất
để dùng thẻ có số hiệu x thu được thẻ có số hiệu y hay không?
Dữ liệu ra là tệp văn bản the.out trình bày theo dạng:
Bỏ thẻ x vào cửa thu được thẻ
Bỏ thẻ vào cửa thu được thẻ y
Nếu không tìm được cách để từ thẻ có số hiệu x thu được thẻ có số hiệu y thì ghi vào tệp thông báo ‘Tu the x khong thu duoc the y’
Ví dụ:
Tệp
Doithe.inp
Tệp Doithe.out
Tệp Doithe.inp
Tệp Doithe.out
Bo the 2 vao cua 1 thu duoc the 3
Bo the 3 vao cua 1 thu duoc the 1 5 4 4 5
Tu the 4 khong thu duoc the 5
Phân tích: Bài toán trên thuộc lớp các bài toán biến đổi trạng thái Hơn nữa yêu cầu tìm kết
quả qua ít bước thao tác nhất nên ta sử dụng thuật toán Loang là thích hợp nhất
Cụ thể như sau:
Ta sử dụng ma trận A[i,j] (i = 1 N, j = 1 M) để lưu giữ thông tin quan hệ giữa thẻ - cửa đã cho trong tệp dữ liệu vào Ta phải hiểu: Bỏ thẻ i vào cửa j thu được thẻ A[i,j]
Cấu trúc dữ liệu hàng đợi được biểu diễn bởi mảng một chiều Q
Ta phải tìm các thao tác sao cho từ thẻ x thu được thẻ y một cách nhanh nhất
Đầu tiên ta cho thẻ x vào Q
Trang 19Cứ mỗi lần lấy một thẻ p ra từ Q ta lại nạp vào Q những thẻ có thể thu được từ thẻ p (chỉ nạp những thẻ chưa có trong Q, hàm vitri(v) cho biết thẻ v có ở trong Q hay không: Giá trị của hàm bằng 0 thì thẻ v chưa có trong Q, ngược lại thì đã có).
Khi nạp một thẻ vào Q ta phải lưu giữ thông tin thẻ đó có được từ thẻ nào và bỏ vào cửa nào
để sau này truy xuất kết quả (mảng một chiều TRUOC có nhiệm vụ đó)
Quá trình trên lặp đi lặp lại cho đến khi hàng đợi Q rỗng (dau > cuoi)
Kết thúc quá trình ‘Loang’, dựa vào mảng TRUOC ta truy xuất kết quả bằng thủ tục đệ quy Ketqua(y)
Khi truy xuất kết quả ta phải hiểu rằng nếy TRUOC[y].the = 0 thì có nghiã là thẻ y không thu được từ một thẻ nào cả
Lưu ý: một thẻ i bỏ vào một của j nào đó thì vẫn có thể thu được thẻ i.
Sau đây là toàn văn chương trình:
A:Array[1 Nmax,1 Mmax] of Byte; {Quan hệ Thẻ - Cửa}
Q:Array[1 Nmax] of Byte; {Hàng đợi - chứa các thẻ thu được từ thẻ x}
Trang 21Bài tập: Cũng bài toán trên nhưng yêu cầu từ thẻ x thu được thẻ có số hiệu lớn nhất một cách
nhanh nhất (thẻ có số hiệu lớn nhất trong các thẻ thu được từ thẻ x)
Bài 5: Đường đi của Robot (Đề thi HSG lớp 12 năm học 2009 - 2010, Tỉnh Hà Tĩnh)
Một bảng hình chữ nhật có kích thước MxN (M,N nguyên dương và không lớn hơn 100) được chia thành các ô vuông đơn vị bằng các đường thẳng song song với các cạnh Một số ô vuông nào đó có thể đặt các vật cản Từ một ô vuông, Robot có thể đi đến một ô vuông kề cạnh với nó nếu ô vuông đó không có vật cản Hỏi rằng nếu Robot bắt đầu xuất phát từ một ô vuông không có vật cản thuộc dòng K, cột L thì có thể đi đến được ô vuông không có vật cản thuộc dòng H, cột O hay không? Nếu có thì hãy chỉ ra đường đi qua ít ô vuông nhất
Dữ liệu vào là tệp văn bản BAI3.INP có cấu trúc:
- Dòng đầu tiên ghi các chữ số M, N, K, L, H, O Các số ghi cách nhau ít nhất một ký tự trống;
- M dòng tiếp theo, mỗi dòng ghi N số 1 hoặc 0 tuỳ thuộc vào ô vuông tương ứng trong bảng hình chữ nhật nêu trên có vật cản hay không (ghi số 1 nếu có vật cản); các số trên mỗi dòng ghi liên tiếp nhau
Dữ liệu ra là tệp văn bản BAI3.OUT có cấu trúc:
Nếu Robot có thể đi được từ ô vuông thuộc dòng K, cột L đến ô vuông thuộc dòng H, cột
O thì:
- Dòng đầu tiên ghi ‘Co duong di ‘;
- Các dòng tiếp theo, mỗi dòng ghi 2 số là chỉ số dòng và chỉ số cột của các ô vuông trong đường đi tìm được từ ô vuông thuộc dòng K, cột L đến ô vuông thuộc dòng H, cột O mà qua ít
ô vuông nhất Hai số trên mỗi dòng ghi cách nhau ít nhất một ký tự trống;
- Ngược lại, nếu Robot không thể đi được từ ô vuông thuộc dòng K, cột L đến ô vuông thuộc dòng H, cột O thì ghi ‘Khong co duong di’
Ví dụ 1:
Tệp robot.inp:
4 7 3 4 2 61000000001010000000001101000
Tệp robot.out:
Khong co duong di
Phân tích: