Thuật toán Loang theo lớp

6 5.3K 184
Thuật toán Loang theo lớp

Đang tải... (xem toàn văn)

Thông tin tài liệu

Thuật toán Loang theo lớp

toán loang theo lớpNguyễn Tuấn DũngTìm kiếm theo chiều rộng (Breadth First Search - BFS) - còn gọi là thuật toán loang và tìm kiếm theo chiều sâu (Depth First Search - DFS) là 2 thuật toán cơ bản trong lý thuyết đồ thị. Mặc dù thuật toán DFS với cấu trúc dữ liệu kiểu ngăn xếp (Stack) tuân theo quy luật LIFO - Last in, First out (vào sau, ra trước) có thể cài đặt dễ dàng bằng đệ quỵ Nhưng vẫn nảy sinh những bài toán mà để giải quyết nó thuận tiện hơn cần có một thuật toán tìm kiếm khác, đó là BFS. Với cấu trúc dữ liệu kiểu hàng đợi (Queue), tuân theo quy luật FIFO - First in, First out (vào trước, ra trước). Cài đặt thuật toán loang không cần dùng đệ quy và cũng không đến nỗi phức tạp, đó là một ưu điểm. Hơn nữa, thuật toán tìm kiếm theo chiều sâu 'đi sâú vào đồ thị, lưu các đỉnh trên đường đi vào ngăn xếp, còn thuật toán tìm kiếm theo chiều rộng 'quét ngang' đồ thị và lưu các đỉnh vào hàng đợị Như ta biết, có thể tồn tại nhiều đường đi từ một đỉnh A đến đỉnh B trên đồ thị. Nếu bằng BFS tìm được một đường đi đầu tiên từ đỉnh A đến đỉnh B thì đường đi đó sẽ là đường đi qua ít đỉnh nhất từ A để đến được B. Nhưng nếu bằng DFS thì ngược lại, đường đi tìm được đầu tiên chưa chắc đã là đường đi qua ít đỉnh nhất. Mà trong một số bài toán, mặc dù vấn đề cần giải quyết có thể được biến đổi, làm cho phức tạp hơn bởi người ra đề nhưng chung qui vẫn có thể đưa về việc tìm đường đi sao cho phải qua ít đỉnh nhất. Khi đó cài đặt thuật toán loang là giải pháp thích hợp. Đối với BFS cơ bản thì các bạn đã từng gặp trên các số báo trước đây của ISM. Tuy nhiên, trong các bài toán cụ thể, mặc dù vẫn với yêu cầu trên nhưng chúng ta lại phải sử dụng thuật toán loang một cách linh hoạt và sáng tạo mới đạt được kết quả mong đợị Một ví dụ minh hoạ cho điều đó được đưa ra trong cách giải bài toán MEET - 'Gặp gỡ' (thi Quốc gia 1998 -1999) dưới đây: Trên một lưới ô vuông M*N (M,N < 100), người ta đặt robot A ở góc trái trên, robot B ở góc phải dướị Mỗi ô của lưới có thể đặt một vật cản hoặc không (ô trái trên và phải dưới không có vật cản). Hai robot bắt đầu di chuyển đồng thời với tốc độ như nhau và không robot nào được dừng lại trong khi robot kia di chuyển (trừ khi nó không thể đi được nữa). Tại mỗi bước, robot chỉ có thể di chuyển theo 4 hướng - đi lên, đi xuống, sang trái, sang phải - vào các ô kề cạnh. Hai robot sẽ gặp nhau nếu chúng đứng trong cùng một ô vuông. Bài toán đặt ra là tìm cách di chuyển ít nhất mà 2 robot phải thực hiện để có thể gặp nhau. Dữ liệu vào trong file Meet.inp : - dòng đầu ghi 2 số M,N.- M dòng tiếp theo, mỗi dòng ghi N số 0 hoặc 1 mô tả trạng thái của các ô vuông: 1-có vật cản, 0-không có vật cản.Các số trên cùng một dòng của file dữ liệu cách nhau ít nhất một dấu trắng.Kết quả ghi ra file Meet.out :- nếu 2 robot không thể gặp nhau thì ghi ký tự #.- Ngược lại, ghi hai dòng, mỗi dòng là một dãy các lý tự viết liền nhau mô tả các bước đi của robot : U-đi lên, D-đi xuống, L- sang trái, R- sang phảị Dòng đầu là các bước đi của A, dòng sau là của B.Ví dụ: Meet.inp 4 6 0 1 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 10 1 0 1 0 0Meet.outDRRRLULU Với dạng bài kiểu này thì ai cũng nghĩ ngay đến thuật tốn loang. Nhưng loang như thế nàỏ vì ta có hai robot cùng di chuyển. Và tất nhiên, bài tốn này trở nên hay hơn so với các bài loang thơng thường. Chắc hẳn ai cũng biết một thí nghiệm phổ biến về sự giao thoa sóng: 'cho 2 chiếc kim (khơng cách xa nhau lắm) dao động cùng tần số trên mặt nước, khi đó có thể quan sát thấy những vòng tròn đồng tâm trên mặt nước cứ loang rộng dần ra cho đến một lúc nào đó, 2 vòng tròn đầu tiên được tạo ra bởi sự dao động của 2 chiếc kim này gặp nhau, giao thoa với nhẳ. Từ thí nghiệm đó, chúng ta có thể đưa ra cách di chuyển cho 2 robot theo kiểu loang chiều rộng, nhưng chỉ loang từng lớp, từng lớp một đối với mỗi robot, giống như 2 vòng tròn loang rộng dần ra trên mặt nước. Về mặt ý tưởng thì sự loang của 2 robot là song song, đồng thời với nhaụ Nhưng bắt tay vào làm cụ thể thì khơng cho phép chúng ta cài đặt thuật tốn theo kiểu song song ở đâỵ Vì thế cách giải quyết của tơi là sử dụng một vòng lặp (Procedure Strati-BFS) thực hiện ln phiên 2 thủ tục: cứ cho robot 1 loang ra một lớp mới xung quanh (Procedure BFSRobot1) thì dừng lại để cho robot 2 cũng loang ra một lớp xung quanh khác (Procedure BFSRobot2). Vòng lặp sẽ kết thúc nếu hai lớp vừa mới loang của 2 robot này giao nhau (PathFound - 2 robot sẽ gặp được nhau) hoặc cả hai robot đều khơng thể di chuyển được nữa (Stoped - bài tốn vơ nghiệm). Tơi tạm gọi đây là thuật tốn 'loang theo lớp'. Bản chất này thực ra đã tồn tại trong cách lặp của thuật tốn loang cơ bản và ở đây, việc khai thác được nó giúp chúng ta giải quyết tốt vấn đề đặt rạ Tuy nhiên, khái niệm lớp ở đây nên hiểu như thế nàỏ Các bạn đã biết trong cách lặp của BFS thì tại mỗi lần lặp, ta sẽ lấy ra (get out) các đỉnh trong hàng đợi từ Queue[first] đến Queue[last] để đưa vào (put in ) hàng đợi các đỉnh khác kề với các đỉnh đã lấy rạ Sau đó các biến first, last sẽ được điều chỉnh để trở thành vị trí đầu và vị trí cuối của đoạn chứa các đỉnh mới này trong Queue (xem Procedure BFSRobot1; và Procedure BFSRobot2;). Chúng ta gọi tập hợp các đỉnh từ Queue[first] đến Queue[last] trong mỗi bước lặp của BFS là một lớp. Dưới đây là chương trình giảị Trong trương trình có sử dụng một số thủ thuật cơ bản khá quen thuộc trong bài tốn tìm đường đi trên lưới ơ vng như kỹ thuật 'ràó - viền xung quanh mảng A bằng các số 1 để khơng phải kiểm tra điều kiện vượt khỏi giới hạn của lưới trong q trình di chuyển robot: A[0,j] = A[M+1,j] = A[i,0] = A[i,N+1] = 1 (i = 1, ., M ; j = 1, . N). Và 2 mảng hằng Hi, Hj giúp ta có thể dùng vòng for để thực hiện 4 hướng đi L, U, R, D dễ dàng hơn trong khi loang, thuận tiện cho việc viết chương trình. Mảng New1 được dùng để đánh dấu các ơ vng thuộc lớp mà robot 1 vừa loang tới, khi cho robot 2 loang nếu gặp phải một trong các ơ này thì nghĩa là tìm được nghiệm của bài tốn. Có thể tiết kiệm bộ nhớ nếu khơng dùng New1, mà đánh dấu ngay bằng mảng A với cách cộng thêm 100 vào A[x,y] nếu ơ (x,y) thuộc lớp mà robot 1 vừa loang tới (vì A[x,y] chỉ ghi tồn số 1 hoặc số 0). Hai mảng Q1, Q2 là hàng đợi cho việc loang robot 1 và robot 2. Nếu cần tiết kiệm bộ nhớ có thể sử dụng chung một hàng đợi Q cho việc loang của 2 robot, robot 1 sử dụng phần đầu từ Q[1] trở đi còn robot 2 sử dụng phần cuối từ Q[max*max] trở lại, vì tổng số ô vuông mà cả 2 robot đi qua không vượt quá kích thước lưới M*N cho đến khi hai lớp loang của 2 robot giao nhau, mà vòng lặp lại được kết thúc ngay tại đây (tôi đã thử cài đặt kiểu này, chương trình vẫn chạy ra kết quả đúng).Còn việc đánh dấu đường đi được thực hiện bởi cách cộng thêm k vào A[x,y] nếu ô(x,y) là ô được đi đến từ ô trước đó theo hướng k (số tự nhiên k chạy từ 1 đến 4 lần lượt chỉ 4 hướng đi của robot: L, U, R, D). xm,ym là toạ độ ô vuông 2 robot gặp nhaụ Phải sử dụng thêm biến pred2 để ghi nhận hướng mà từ đó Robot 2 đi tới (xm,ym) vì A (xm,ym) đã dùng để ghi nhận hướng mà từ đó Robot 1 đi đến ô gặp nhau. Việc tìm lại đường đi cho từng robot được thực hiện trong procedure FindS1S2.Trong khuôn khổ bài báo, không thể giải thích kỹ chương trình, vì vậy các bạn có thể nghiên cứu chi tiết thêm qua chương trình giải cụ thể dưới đây: Uses Crt;Const Inf = 'Meet.inp';Outf = 'Meet.out';Max = 100;Hi : Array[1 4]of integer = (0,-1,0,1);Hj : Array[1 4]of integer = (-1,0,1,0);Type Square=record x,y:byte; end;Var A : Array[0 Max+1,0 Max+1]of byte;Q1,Q2 : Array[1 Max*Max]of square;New1 : Array[0 Max+1,0 Max+1]of Boolean;First1,Last1,First2,Last2,N,M,Pred2,Xm,Ym : Word;PathFound,Stoped : boolean;S1,S2 : string;Procedure Readinp;var f:text; i,j:word;Beginfillchar(A,sizeof(A),1); {rào xung quanh}assign(f,inf); reset(f);readln(f,M,N);for i:=1 to M dobeginfor j:=1 to N do read(f,a[i,j]);readln(f);end;close(f);End;Procedure Init;Beginfirst1:=1; last1:=1;Q1[1].x:=1; Q1[1].y:=1;first2:=1; last2:=1;Q2[1].x:=M; Q2[1].y:=N; End;Procedure BFSRoBot1;var x,y,i,j,k:word;beginj:=last1;for i:=first1 to last1 dofor k:=1 to 4 dobeginx:=Q1[i].x + Hi[k];y:=Q1[i].y + Hj[k];if A[x,y]=0 thenbegininc(A[x,y],k);inc(j);Q1[j].x:=x; Q1[j].y:=y;New1[x,y]:=true;end;end;first1:=last1+1; last1:=j;if first1>last1 then Stoped:=true;end;Procedure BFSRoBot2;var x,y,i,j,k:word;beginj:=last2;for i:=first2 to last2 dobeginfor k:=1 to 4 dobeginx:=Q2[i].x + Hi[k];y:=Q2[i].y + Hj[k];if A[x,y]=0 thenbegininc(A[x,y],k);inc(j);Q2[j].x:=x; Q2[j].y:=y;end;if New1[x,y] then beginPathFound:=true;Pred2:=k;xm:=x; ym:=y;Exit;end;end;end; first2:=last2+1; last2:=j;if first2>last2 then Stoped:=true;end;Procedure StratiBFS;BeginPathFound:=False;Stoped:=False;Repeatfillchar(New1,sizeof(New1),false);BFSRoBot1;BFSRoBot2;Until Pathfound or Stoped;End;Procedure FindS1S2;var x,y,k:byte;begins1:=''; s2:='';x:=xm; y:=ym;{RoBot1}repeatk:=A[x,y];case k of1 : s1:=s1+'L';2 : s1:=s1+'U';3 : s1:=s1+'R';4 : s1:=s1+'D';end;x:=x-Hi[k];y:=y-Hj[k];until (x=1)and(y=1);{RoBot2}x:=xm; y:=ym; A[x,y]:=Pred2;repeatk:=A[x,y];case k of1 : s2:=s2+'L';2 : s2:=s2+'U';3 : s2:=s2+'R';4 : s2:=s2+'D';end;x:=x-Hi[k];y:=y-Hj[k];until (x=M)and(y=N);end;Procedure WriteOut;var f:text; i,j:byte; beginassign(f,outf); rewrite(f);If not(pathfound) thenbeginwrite(f,'#'); close(F); exit;end;FindS1S2;for i:=length(s1) downto 1 do write(f,s1[i]);writeln(f);for i:=length(s2) downto 1 do write(f,s2[i]);close(f);end;BEGINReadinp;Init;StratiBFS;WriteOut;END.Chúng ta cần chú ý rằng, bài toán có thể có nhiều nghiệm vì tồn tại nhiều cách đi từ góc trái trên đến góc phải dướị Hoặc, tuỳ thuộc vào trạng thái của lưới, hai robot sẽ có thể không bao giờ gặp nhaụ Trường hợp đó xảy ra khá hiển nhiên khi không tồn tại đường đi từ ô(1,1) đến ô(M,N), hoặc tồn tại, nhưng đường đi đó có độ dài chẵn (ta tạm gọi độ dài đường đi là tổng số đỉnh trên đường đi đó). Vì hai robot chỉ có thể gặp nhau nếu như có một đường đi độ dài lẻ từ ô(1,1) đến ô(M,N). Có thể chứng minh điều này: giả sử 2 robot gặp nhau ở ô(xm,ym) thì đường đi từ ô(1,1) đến ô(xm,ym) phải bằng đường đi từ ô (M,N) đến ô (xm,ym) và giả sử bằng d , khi đó tổng độ dài đường đi từ ô (1,1) đến ô (M,N) sẽ bằng (2d - 1) là một số lẻ. Như vậy, đây cũng là một hướng để giải quyết bài toán này, tức là tìm đường đi độ dài lẻ ngắn nhất từ góc trái trên đến góc phải dướị Đó chính là con đường để hai robot gặp nhau mà số bước di chuyển cần thiết là ít nhất. Tuy nhiên, tôi đã không tìm lời giải theo cách đó, phần này dành cho bạn đọc tự làm thêm. ý tưởng loang theo từng lớp ở trên, không chỉ để giải bài toán “Meet” mà hy vọng rằng nó còn có tác dụng trong các ứng dụng khác của bạn. . toán loang theo lớpNguyễn Tuấn DũngTìm kiếm theo chiều rộng (Breadth First Search - BFS) - còn gọi là thuật toán loang và tìm kiếm theo chiều. trước). Cài đặt thuật toán loang không cần dùng đệ quy và cũng không đến nỗi phức tạp, đó là một ưu điểm. Hơn nữa, thuật toán tìm kiếm theo chiều sâu 'đi

Ngày đăng: 11/09/2012, 15:00

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan