You are on page 1of 7

FLOOD FILL

(TÔ MÀU LOANG)


1. ĐỊNH NGHĨA
Flood fill (hay Seed fill) là thuật toán xác định miền liên thông từ một node, trong mảng n chiều (n≥2).
* Chú ý: Trong phạm vi tài liệu, ta xem xét flood fill trong mảng 2 chiều.
2. FLOOD FILL TRÊN BẢNG 2 CHIỀU
Xét bảng gồm có m dòng, n cột. Khi đó từ 1 ô bất kì có thể loang đến các ô khác. Ở đây ta xét 2 trường hợp
sau:
• Loang 4 hướng (từ 1 ô có thể đi đến 4 ô kề)
• Loang 8 hướng (từ 1 ô có thể đi đến 8 ô kề)

Tương ứng với mỗi trường hợp, ta có 2 cách cài đặt:


• Loang theo chiều sâu (ứng dụng stack)
• Loang theo chiều rộng (ứng dụng queue)
LOANG 4 HƯỚNG
Miền 0 là tập hợp các ô số 0 có chung cạnh. Hãy viết chương trình đếm các miền 0.
Input: File loang.inp, trong đó
• Dòng đầu tiên cho biết số dòng m, số cột n
• M dòng tiếp theo biểu diễn bảng ô vuông gồm các số 0 hoặc 1
Output: File loang.out, trong đó
• Gồm một dòng duy nhất chứa số k cho biết số miền 0

Loang.inp Loang.out
4 7 2
0 0 1 0 1 1 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0

PHÂN TÍCH BÀI TOÁN


Đây là dạng bài toán toán loang theo 4 hướng, trong bài toán này ta cần giải quyết các vấn đề sau:
• Khi nào quá trình loang bị chặn lại?
• Loang 4 hướng như thế nào (theo chiều rộng hay chiều sâu)?

CÀI ĐẶT CHƯƠNG TRÌNH


Khi nào quá trình loang bị chặn lại?
Robot chỉ có thể di chuyển trên các ô số 0, nên quá trình loang bị chặn lại khi gặp:
• các ô chứa số 1 (ô khác 0)
• 4 bức tường

Đoạn chương trình:


If a[i,j]<>0 then exit;
If (i<1) OR (i>m) OR (j<1) OR (j>n) then exit;
Loang 4 hướng như thế nào?
Theo chiều sâu:
Cài đặt 1:
Procedure Loang(i,j:integer);
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
If (i<1) OR (i>m) OR (j<1) OR (j>n) then exit;
a[i,j]:=2; { Đánh dấu đã chọn }

{Phần đệ quy}
Loang(i,j+1); {Loang theo hướng ĐÔNG}
Loang(i,j-1); {Loang theo hướng TÂY}
Loang(i+1,j); {Loang theo hướng NAM}
Loang(i-1,j); {Loang theo hướng BẮC}
End;

Cài đặt 2: Sử dụng vòng lặp để cải tiến cài đặt 1


Procedure Loang(i,j:integer);
Var i1,j1:integer;
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
If (i<1) OR (i>m) OR (j<1) OR (j>n) then exit;
a[i,j]:=2; { Đánh dấu đã chọn }

{Phần đệ quy}
For i1:=-1 to 1 do
For j1:=-1 to 1 do
If (i1*j1=0) AND (i1+j1<>0) then {Loang 4 hướng}
Loang(i+i1,j+j1);
End;

Cài đặt 3: Cải tiến cài đặt 2 bằng cách sử dụng hằng mảng
Const
dx:array[1..4] of shortint=(-1,0,1,0);
dy:array[1..4] of shortint=(0,-1,0,1);

Procedure Loang(i,j:integer);
Var k:integer;
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
If (i<1) OR (i>m) OR (j<1) OR (j>n) then exit;
a[i,j]:=2; { Đánh dấu đã chọn }

{Phần đệ quy}
For k:=1 to 4 do
Loang(i+dx[k],j+dy[k]);
End;

Cài đặt 4: Xử lý 4 bức tường theo cách khác


Const
dx:array[1..4] of shortint=(-1,0,1,0);
dy:array[1..4] of shortint=(0,-1,0,1);
Var a:array[0..max+1, 0..max+1] of integer; {Lưu ý chỉ số bắt đầu từ 0}
Procedure Nhap;
Begin
….
Fillchar(a, sizeof(a),-1);
….
For i:=1 to m do
Begin
For j:=1 to n doRead(fi, a[i,j]);
Readln(fi);
End;
End;
Procedure Loang(i,j:integer);
Var k:integer;
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
a[i,j]:=2; { Đánh dấu đã chọn }

{Phần đệ quy}
For k:=1 to 4 do
Loang(i+dx[k],j+dy[k]);
End;

Theo chiều rộng (sử dụng queue)


Vì mỗi ô xác định thông qua chỉ số dòng và chỉ số cột, nên ta sẽ sử dụng 2 mảng 1 chiều queue để xử lý.
Cài đặt 1:
Const
dx:array[1..4] of shortint=(-1,0,1,0);
dy:array[1..4] of shortint=(0,-1,0,1);

Procedure LoangBFS(i,j:integer);
Var
qx, qy:queue; {qx: hang doi luu chi so dong,
qy: hang doi luu chi so cot}
head,tail:integer;
x,y,k:integer;
Begin
head:=1; tail:=1;
qx[1]:=i; qy[1]:=j; {Them phan tu vao hang doi}
a[i,j]:=2; {Danh dau da chon}
Repeat
x:=qx[head]; y:=qy[head]; {Lay phan tu dau hang doi}
head:=head+1; {Xoa phan tu khoi hang doi}
For k:=1 to 4 do {Xet 4 O ke voi O(x,y)}
if a[x+dx[k],y+dy[k]]=0 then {Neu co gia tri bang 0}
Begin {Them vao hang doi}
tail:=tail+1;
qx[tail]:=x+dx[k]; qy[tail]:=y+dy[k];
a[x+dx[k],y+dy[k]]:=2; {Danh dau da chon}
End;
Until head>tail; {Hang doi rong}
End;
Cài đặt 2: Ta sẽ không sử dụng biến phụ x, y trong thủ tục LoangBFS, mà sử dụng trực tiếp phần tử đầu hàng
đợi thay cho biến x, y
Const
dx:array[1..4] of shortint=(-1,0,1,0);
dy:array[1..4] of shortint=(0,-1,0,1);

Procedure LoangBFS(i,j:integer);
Var
qx, qy:queue; {qx: hang doi luu chi so dong,
qy: hang doi luu chi so cot}
head,tail:integer;
k:integer;
Begin
head:=1; tail:=1;
qx[1]:=i; qy[1]:=j;
a[x,y]:=2; {Danh dau da chon}
Repeat
For k:=1 to 4 do {Xet 4 O ke voi O(x,y)}
if a[qx[head]+dx[k],qy[head]+dy[k]]=0 then {neu co gia tri 0}
Begin {Them phan tu vao hang doi}
tail:=tail+1;
qx[tail]:=qx[head]+dx[k]; qy[tail]:=qy[head]+dy[k];
a[qx[tail],qy[tail]]:=2; {Danh dau da chon}
End;
head:=head+1; {Xoa phan tu dau hang doi}
Until head>tail; {Hang doi rong}
End;

LOANG 8 HƯỚNG
Nếu miền 0 là tập hợp các ô 0 có chung đỉnh, khi đó từ 1 ô có thể loang đến 8 ô kề với nó.
Theo chiều sâu:
Cài đặt 1: Sử dụng vòng lặp để cải tiến cài đặt 1
Procedure Loang(i,j:integer);
Var i1,j1:integer;
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
If (i<1) OR (i>m) OR (j<1) OR (j>n) then exit;
a[i,j]:=2; { Đánh dấu đã chọn }
{Phần đệ quy}
For i1:=-1 to 1 do
For j1:=-1 to 1 do
If (i1<>0) OR (j1<>0) then {Loang 8 hướng}
Loang(i+i1,j+j1);
End;

Cài đặt 2: Cải tiến cài đặt 1 bằng cách sử dụng hằng mảng
Const
dx:array[1..8] of shortint=(-1,0,1,0,-1,1,-1,1);
dy:array[1..8] of shortint=(0,-1,0,1,1,-1,-1,1);
Var a:array[0..max+1, 0..max+1] of integer; {Lưu ý chỉ số bắt đầu từ 0}

Procedure Nhap;
Begin
….
Fillchar(a, sizeof(a),-1);
….
For i:=1 to m do
Begin
For j:=1 to n doRead(fi, a[i,j]);
Readln(fi);
End;
End;

Procedure Loang(i,j:integer);
Var k:integer;
Begin
{Phần cơ sở}
If a[i,j]<>0 then exit;
a[i,j]:=2; { Đánh dấu đã chọn }

{Phần đệ quy}
For k:=1 to 8 do {Loang 8 hướng}
Loang(i+dx[k],j+dy[k]);
End;

Theo chiều rộng (sử dụng queue)


Const
dx:array[1..8] of shortint=(-1,0,1,0,-1,1,-1,1);
dy:array[1..8] of shortint=(0,-1,0,1,1,-1,-1,1);
Procedure LoangBFS(i,j:integer);
Var
qx, qy:queue; {qx: hang doi luu chi so dong,
qy: hang doi luu chi so cot}
head,tail:integer;
k:integer;
Begin
head:=1; tail:=1;
qx[1]:=i; qy[1]:=j;
a[i,j]:=2; {Danh dau da chon}
Repeat
For k:=1 to 8 do {Xet 8 O ke voi O(x,y)}
if a[qx[head]+dx[k],qy[head]+dy[k]]=0 then {neu co gia tri 0}
Begin {Them vao hang doi}
tail:=tail+1;
qx[tail]:=qx[head]+dx[k]; qy[tail]:=qy[head]+dy[k];
a[qx[tail],qy[tail]]:=2; {Danh dau da chon}
End;
head:=head+1; {Xoa phan tu dau hang doi}
Until head>tail; {Hang doi rong}
End;

BÀI TẬP
Bài 1. RÔ BỐT (Tên file robot.pas)
Miền 0 là tập hợp các ô số 0 có chung cạnh. Một rô bốt chỉ có thể di chuyển trong miền 0. Rô bốt bắt đầu xuất
phát từ ô (x1, y1) và đi đến ô (x2, y2). Hãy lập trình tính số bước di chuyển ít nhất từ ô (x1, y1) đến ô (x2, y2),
và chỉ ra một cách đi như vậy.
Input: File robot.inp, trong đó
• Dòng đầu tiên cho biết số dòng m, số cột n,
• Dòng thứ 2 gồm 4 số nguyên x1, y1, x2, y2
• M dòng tiếp theo biểu diễn bảng ô vuông gồm các số 0 hoặc 1
Output: File robot.out, trong đó
Nếu đi được
• Dòng đầu tiên chứa số k cho biết số bước di chuyển ngắn nhất
• Dòng thứ 2 cho biết cách di chuyển, trong đó
o R: qua phải 1 ô
o D: xuống dưới 1 ô
o L: qua trái 1 ô
o U: lên trên 1 ô
Ngược lại không đi được thì xuất dòng chữ “khong di duoc”

robot.inp robot.out
4 7 4
1 1 3 3 RDDR
0 0 1 0 1 1 0
0 0 1 1 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
4 6 khong di duoc
4 4 1 1
0 0 1 1 1 1
0 1 1 1 1 0
0 1 1 0 0 0
0 1 0 0 0 0

Gợi ý: Loang theo chiều rộng

Var v:array[1..max,1..max] of integer; {LUU VET DUONG DI}


Procedure LoangBFS(x1,y1,x2,y2:integer);
Var
qx, qy:queue; {qx: hang doi luu chi so dong,
qy: hang doi luu chi so cot}
head,tail:integer;
k:integer;
Begin
Fillchar(v, sizeof(v), 0);
head:=1; tail:=1;
qx[1]:= x1; qy[1]:=y1;
v[x1,y1]:=-1; {Danh dau da chon, diem xuat phat}
Repeat
For k:=1 to 4 do {Xet 4 O ke voi O(x,y)}
if a[qx[head]+dx[k],qy[head]+dy[k]]=0 then {co gia tri 0}
Begin {Them vao hang doi}
tail:=tail+1;
qx[tail]:=qx[head]+dx[k]; qy[tail]:=qy[head]+dy[k];
v[qx[tail],qy[tail]]:=k; {Luu huong duong di}
End;
head:=head+1; {Xoa phan tu dau hang doi}
Until (head>tail) {Hang doi rong}
OR (v[x2,y2]<>0); {Den duoc x2, y2}
End;

BÀI 2. GẶP GỠ (Tên file GAP.pas)


Trên một lưới ô vuông kích thước MxN (M dòng, N cột, ≤100) người ta đặt hai con robot A và B. Robot
A được đặt ở góc trên bên trái, còn robot B được đặt ở góc dưới bên phải. Mỗi ô của lưới có thể đặt một vật
cản hay không. Tại mỗi bước, mỗi robot chỉ có thể di chuyển theo các hướng lên, xuống, trái, phải – vào các ô
kề cạnh không có vật cản. Hai robot sẽ gặp nhau nếu chúng cùng đứng trong một ô. Hai robot bắt đầu di
chuyển đồng thời và mỗi lượt cả hai đều phải thực hiện di chuyển (nghĩa là không cho phép một robot dừng lại
một ô nào đó trong khi robot kia thực hiện bước di chuyển). Bài toán đặt ra là tìm số bước di chuyển ít nhất mà
hai robot phải thực hiện để có thể gặp nhau. Chú ý rằng, tùy trạng thái của lưới, hai robot có thể không khi nào
gặp được nhau.
Dữ liệu vào cho bởi file văn bản GAP.INP, cấu trúc như sau:
• Dòng đầu tiên chứa hai số M và N
• M dòng tiếp theo, mỗi dòng ghi N số gồm 0 và 1 mô tả trạng thái dòng tương ứng của lưới, trong đó
mỗi số mô tả một ô với quy ước: 0-không có vật cản, 1-có vật cản.
Các số trên cùng một dòng của file dữ liệu được ghi cách nhau ít nhất một dấu cách.
Kết quả ghi lên file GAP.OUT:
• Nếu hai robot không thể gặp nhau thì ghi một dòng gồm một kí tự #.
• Trái lại ghi hai dòng, mỗi dòng là một dãy các kí tự viết liền nhau mô tả các bước đi của robot: U-lên
trên, D-xuống dưới, L-sang trái, R-sang phải, dòng đầu là bước đi của robot A và dòng sau là các bước
đi của robot B.
Ví dụ:
GAP.INP GAP.OUT
46 DRRR
011000 LUUL
000001
001001
010100

Gợi ý: Sử dụng thuật toán loang theo chiều rộng.

BÀI 3. CỜ VÂY
Trên lưới ô vuông tạo thành từ N đường dọc và N đường ngang, hai người lần lượt đặt quân của mình
trên các nút của lưới. Các đường được đánh số từ trên xuống dưới và từ trái qua phải bắt đầu từ 1. Một người
cầm quân đen, người kia cầm quân trắng.
Nhóm quân cùng màu là các quân trên các nút tạo thành một miền liên thông khi chuyển động theo
chiều dọc hoặc ngang. Một quân hoặc nhóm quân cùng màu gọi là bị vây nếu mọi nút kề với các quân này đều
có quân khác màu. Những quân bị vây sẽ bị đối phương ăn (lấy ra khỏi bàn cờ).
Trên hình bên trái có 2 nhóm quân trắng có nguy cơ bị vây. Người cầm quân đen có thể đặt quân của
mình vào vị trí (5,1) và ăn được 3 quân hoặc đặt vào vị trí (4,8) và ăn được 7 quân. Hình bên phải là kết quả
quân đen đặt vào vị trí (4,8).

Yêu cầu: Cho N và cấu hình quân trên bàn cờ. Hãy xác định một vị trí đặt quân đen để có thể ăn được nhiều
quân nhất các quân trắng tại nước đi đó, giả thiết rằng luôn tồn tại ít nhất một nước đi như vậy.
Dữ liệu: Trong file văn bản ENCIRCLE.INP gồm
- Dòng đầu tiên chứa số nguyên N (8 ≤ N ≤ 100).
- N dòng sau, mỗi dòng chứa một xâu N ký tự xác định trạng thái dòng tương ứng của bàn cờ, quân đen được
ký hiệu bằng ký tự b, trắng − w, vị trí trống − dấu chấm ’.’
Chú ý: Không xét những nhóm quân trắng đã bị vây từ trước.
Kết quả: Ra file văn bản ENCIRCLE.OUT một dòng chứa số nguyên M là số quân trắng nhiều nhất có thể ăn
được tại nước đi tiếp theo của quân đen.
Ví dụ: ouput cua bai tren là 7

You might also like