Hình chữ nhật tối đại trong ma trận 0/1



Hình chữ nhật tối đại trong ma trận 0/1.
Cho một ma trận biểu diễn bằng một mảng hai chiều kích thước N ´ M ô và chỉ chứa các kí tự 0 và 1. Tìm hình chữ nhật chứa toàn kí tự 1 và có diện tích lớn nhất (gọi là hình chữ nhật tối đại).
Dữ liệu vào: Tệp văn bản CNMAX.INP:
-        Dòng đầu tiên: số tự nhiên  M,
3 £ M £ 70
-        Tiếp đến là các dòng dài bằng nhau thể hiện một xâu gồm M kí tự là các chữ cái 0/1 viết liền nhau.
-        Số dòng của tệp input có thể lên tới 60 nghìn.
Dữ liệu ra: tệp văn bản CNMAX.OUT:
-        Dòng đầu tiên: Diện tích của hình chữ nhật tối đại ABCD chứa toàn kí tự 1 tìm được.
-        Dòng thứ hai: Toạ độ dòng và cột của đỉnh A.
-        Dòng thứ ba: Toạ độ dòng và cột của đỉnh C.

j
k
l
m
n
o
p
q
r
u
1
0
0
0
0
0
0
0
0
v
1
1
1
1
1
1
1
1
0
w 
0
0
1
1
1
1
1
0
0
x
0
0
1
1
1
1
1
0
0
y
0
0
0
0
0
0
0
0
0

Hình 4
Thí dụ, với ma trận 5 ´ 9 chứa dữ liệu như hình 4 thì hình chữ nhật tối đại, tạm gọi là ABCD, có diện tích là 15 ô và có toạ độ điểm A là (dòng 2, cột 3) và điểm C là (dòng 4, cột 7) như trong hình 4.
CNMAX.INP
CNMAX.OUT

9
100000000
111111110
001111100
001111100
000000000

15
2 3
4 7

Chúng ta sẽ xây dựng một thuật toán giải bài toán tổng quát hơn như sau:
Bài toán 8.3.1. Sân bay vũ trụ
Người ta cần xác định một vùng hình chữ nhật ABCD có diện tích lớn nhất và bằng phẳng trên hành tinh Vega để xây dựng sân bay vũ trụ. Bạn được cung cấp một mảnh bản đồ hành tinh Vega, nơi cần xác định vị trí xây dựng một sân bay. Mảnh bản đồ có dạng hình chữ nhật gồm nhiều dòng điểm mã số từ 1 trở đi, mỗi dòng có đúng M điểm mã số từ 1 đến M. Mỗi điểm được tô một màu thể hiện độ cao của điểm đó. Yêu cầu: xác định hình chữ nhật ABCD chứa nhiều điểm đồng màu nhất.
Dữ liệu vào: Tệp văn bản CNMAX.INP.
-          Dòng đầu tiên: số tự nhiên M, 3 £M £ 70.
-          Tiếp đến là các dòng dài bằng nhau thể hiện một xâu gồm M kí tự là các chữ cái a..z viết liền nhau. Mỗi kí tự biểu diễn cho một màu thể hiện độ cao của điểm tương ứng trên mảnh bản đồ hành tinh Vega. Hai kí tự khác nhau thể hiện hai độ cao khác nhau. Hai điểm cùng độ cao được biểu diễn với cùng một kí tự.
-          Số dòng của tệp input có thể lên tới 60 nghìn.
Thí dụ:
CNMAX.INP
CNMAX.OUT

20
bcccddddeabcvvvvvvvb
bbbbbccccccccccbbbbb
vvvvvcccccccccccccbb
vvcccccccccccccbbbbb
pppppccccccccccabbbb
pppppcccccccccczzzzz
ssccccccccccccczzzzz
sssssccccccccccccczz
hhhhhcccccccccczzzzz
uuuuuuuuczzzzzzzzzzz

80
2 6
9 15

Dữ liệu ra: tệp văn bản CNMAX.OUT:
-          Dòng đầu tiên: Diện tích của hình chữ nhật tối đại ABCD tìm được.
-          Dòng thứ hai: Toạ độ dòng và cột của đỉnh A (ô Tây-Bắc).
-          Dòng thứ ba: Toạ độ dòng và cột của đỉnh C (ô Đông-Nam).
Tính tổng quát của bài toán 8.3.1 thể hiện ở điểm sau:
-          Tệp không chỉ chứa các kí tự 0/1.
Bài giải
Do không thể đọc toàn bộ dữ liệu từ tệp CMAX.INP vào một mảng để xử lí nên chúng ta sẽ đọc mỗi lần một dòng vào biến kiểu xâu kí tự (string) y. Vì hình chữ nhật ABCD cần tìm chứa cùng một loại kí tự cho nên các dòng của hình sẽ liên thông nhau. Để phát hiện tính liên thông chúng ta cần dùng thêm một biến kiểu xâu kí tự x để lưu dòng đã đọc và xử lí ở bước trước. Tóm lại là ta cần xử lí đồng thời hai dòng: xdòng trướcydòng đang xét.
Nếu xét các cột trong hình chữ nhật cần tìm ta thấy chúng phải chứa cùng một loại kí tự. Ta dùng một mảng hvới ý nghĩa phần tử h[i]của mảng cho biết tính từ vị trí thứ icủa dòng y trở lên có bao nhiêu kí tự giống nhau (và giống với kí tự y[i]). Ta gọi h[i] là độ cao của cột i và mảng h khi đó sẽ được gọi là độ cao của dòng đang xét.
Thí dụ, mảng tích luỹ độ cao của dòng thứ 5 trong thí dụ đã cho sẽ là:
h = (1,1,1,1,1,4,4,4,4,4,4,5,4,4,4,1,2,2,4,5)
A






B








































C






D
Biết độ cao, với hai dòng xy chúng ta dễ dàng tính được diện tích của mỗi hình chữ nhật ABCD chứa phần tử y[i] trên cạnh DC. Thật vậy, giả sử ta đang xét kí tự thứ i = 8 trên dòng thứ 5 như đã nói ở phần trên. Ta có h[8] = h[7] = h[6] = 4; h[9] = h[10] = h[11] = 4; h[12] = 5; h[13] = h[14] = h[15] = 4; h[16] = 1,... Vậy thì, khi ta đi từ i về hai phía, trái và phải, nếu gặp các kí tự giống kí tự y[i] còn độ cao thì không nhỏ hơn h[i] ta sẽ thu được hình chữ nhật lớn nhất chứa kí tự y[i].
Với dòng thứ 5 là y đang xét, ta có:
x = 'vvcccccccccccccbbbbb'; {dòng thu 4 }
y = 'pppppccccccccccabbbb'; {dòng thu 5 }

dòng

1
2
3
4
5
6
7
q
9
10
11
12
13
14
15
16
17
18
19
20
4
x
v
v
c
c
c
c
c
c
c
c
c
c
c
c
c
b
b
b
b
b
5
y
p
p
p
p
p
c
c
c
c
c
c
c
c
c
c
a
b
b
b
b

h
1
1
1
1
1
4
4
4
4
4
4
5
4
4
4
1
2
2
4
5






















trong đó x chứa dữ liệu của dòng thứ 4, y chứa dữ liệu của dòng thứ 5 trong tệp CNMAX.INP.
Từ điểm i = 8dịch chuyển về bên trái ta thu được điểm c1 = 6; dịch chuyển về bên phải ta thu được điểm c2 = 15. Điều kiện để dịch chuyển là:
s         (y[c1] = y[i]) and (h[c1] ³ h[i]), nếu qua trái và
s         (y[c2] = y[i]) and (h[c2] ³ h[i]), nếu qua phải.
Hai thao tác trên được đặt trong hàm tính diện tích của hình chữ nhật ABCD lớn nhất chứa điểm y[i]. Hàm cho ra ba giá trị:
s         c1: điểm trái nhất hay là toạ độ cột của đỉnh D.
s         c2: điểm phải nhất hay là toạ độ cột của đỉnh C.
s         Diện tích của hình.
(*-------------------------------------------
       Dien tich cua hinh chua diem y[i]
--------------------------------------------*)
function DienTich(i: byte; var c1,c2: byte): longint;
begin
{Qua trai}
c1  :=  i;
       while (y[c1-1] = y[i]) and (h[c1-1] >= h[i])
do dec(c1);
{Qua phai}
c2  :=  i;
      while (y[c2+1] = y[i]) and (h[c2+1] >= h[i])
do inc(c2);
DienTich  :=  h[i]*(c2 + 1 - c1);
end;
Phần xử lí chính được đặt trong thủ tục Run.
Mảng h[1..m] lúc đầu được khởi trị toàn 0. Sau mỗi lần đọc dòng y ta chỉnh lại độ cao h theo tiêu chuẩn sau.
Tại điểm i đang xét trên dòng y, nếu y[i] = x[i] độ cao h[i] được tăng thêm 1. Ngược lại, nếu y[i] ¹ x[i] ta đặt lại độ cao h[i] = 1.
{Chinh do cao}
for i :=  1 to m do
if y[i] = x[i] then inc(h[i]) else h[i] :=  1;
Một vài chú giải
1.       Chúng ta sử dụng phần tử h[0] = 0 và h[m+1] = 0 để chặn vòng lặp, cụ thể là để các điều kiện (h[c1-1] >= h[i]) và (h[c2+1] >= h[i]) trở thành false ở cuối vòng lặp.
2.       Một con đếm dòng d kiểu longint cho biết ta đang xử lí dòng nào của tệp.
3.       Dòng x lúc đầu được khởi trị toàn dấu cách là kí tự không có trong văn bản thể hiện tấm bản đồ.
4.       Mỗi khi xử lí xong dòng y ta cần sao chép giá trị của ycho x để lưu giữ cho bước tiếp theo. Khi đó x sẽ trở thành dòng trước.
5.       Mỗi khi tìm được một hình chữ nhật, ta so sánh diện tích của hình với diện tích lớn nhất hiện có (dtmax) để luôn luôn đảm bảo rằng dtmax chính là diện tích lớn nhất trong vùng đã khảo sát.
Thủ tục Run được thiết kế theo sơ đồ sau:
1.       Mở tệp dữ liệu CNMAX.INP;
2.       Đọc giá trị chiều dài mỗi dòng vào biến m;
3.       Khởi trị:
s         Con đếm dòng d :=  0;
s         Dòng trước x toàn dấu cách;
s         Mảng chiều cao h toàn 0;
s         dtmax :=  0; {diện tích max}
4.       Lặp cho đến khi hết tệp CNMAX.INP:
4.1. Đọc dòng y;
4.2. Tăng con đếm dòng d;
4.3. Chỉnh độ cao h[1..m];
4.4. Xử lí mỗi kí tự y[i] của dòng y; i = 1..m.
4.4.1. Tìm diện tích dt của hình chữ nhật lớn nhất chứa phần tử y[i]; cho giá trị ra là dt và hai chỉ số đầu trái c1 và đầu phải c2 thể hiện cạnh đáy của hình chữ nhật.
4.4.2. Nếu dt > dtmax: chỉnh lại các giá trị
Diện tích max: dtmax :=  dt;
Toạ độ đỉnh A(Axmax, Aymax):
Axmax :=  d – h[i] + 1;
Aymax :=  c1;
Toạ độ đỉnh C(Cxmax , Cymax):
Cxmax :=  d;
Cymax :=  c2;
4.5. Sao chép dòng y sang x: x :=  y;
5.       Đóng tệp CNMAX.INP.
6.       Thông báo kết quả.
procedure Run;
var i,c1,c2: byte;
dt: longint;
begin
assign(f,fn); reset(f); readln(f,m);
d :=  0;
{Khoi tri cho dong dau tien}
x :=  BL;
for i :=  1 to m do x :=  x + BL;
{Khoi tri cho chieu cao}
fillchar(h,sizeof(h),0);
while not eof(f) do
begin
 readln(f,y);
 inc(d);
 {Chinh do cao}
 for i  :=   1 to m do
          if y[i] = x[i] then inc(h[i]) else h[i]  :=   1;
 for i  :=   1 to m do
begin
dt  :=   DienTich(i,c1,c2);
if dt > dtmax then
begin
 dtmax   :=   dt;
 Axmax  :=  d-h[i]+1; Aymax  :=  c1;
 Cxmax  :=  d; Cymax  :=  c2;
end;
end;
x  :=  y;
end;
close(f);
Ket; {ghi ket qua}
end;
Độ phức tạp tính toán
Giả sử tệp CNMAX.INP chứa n dòng, mỗi dòng chứa m kí tự. Khi xử lí một dòng, tại mỗi kí tự thứ itrên dòng đó ta dịch chuyển qua trái và qua phải, tức là ta phải thực hiện tối đa m phép duyệt. Vậy, với mỗi dòng gồm m kí tự ta phải thực hiện m­2 phép duyệt. Tổng cộng, với n dòng ta thực hiện tối đa t = n.m­2 phép duyệt.
(*  Pascal  *)
(******************************************
   CNMAX – Dien tich hinh chu nhat
           toi dai
******************************************)
uses crt;
const
fn = 'CNMAX.INP'; {Ten tep chua du lieu vao}
gn = 'CNMAX.OUT'; {Ten tep chua du lieu ra}
MN = 80; {chieu rong van ban}
BL = #32; {dau cach}
NL = #13#10; {xuong dong moi}
var f,g: text;
m: byte; {chieu rong tam ban do}
d: longint; {dem dong}
x,y: string; {x - dong tren}
             {y – dong duoi}
h: array[0..MN] oflongint;
{chieu cao cua cac cot}
dtmax: longint; {Dien tich max}
Axmax, Cxmax: longint; {toa dodiem A, C}
Aymax,Cymax: byte;
(*------------------------------
       Ghi file ket qua
--------------------------------*)
procedure Ket;
begin
assign(g,gn); rewrite(g);
writeln(g,Dtmax);
writeln(g,Axmax,BL,Aymax);
writeln(g,Cxmax,BL,Cymax);
close(g);
end;
(*--------------------------------------------
      Dien tich cua hinh chua diem y[i]
---------------------------------------------*)
function DienTich(i: byte; varc1,c2: byte): longint;
tự viết
procedure Run; tự viết
BEGIN
  run;
END.


Ứng dụng
Bài toán tìm hình chữ nhật tối đại thường dùng trong lĩnh vực đồ hoạ và xử lí ảnh. Dưới đây liệt kê vài ứng dụng điển hình.
1.       Trong khi vẽ bản đồ ta thường phải tìm một hình chữ nhật tối đại trong một vùng, chẳng hạn lãnh thổ của một quốc gia để có thể viết các kí tự vào đó như tên quốc gia, tên châu lục.
2.       Trong hình học phân hình (fractal) ta thường phải tìm một số hình vuông hoặc chữ nhật tối đại thoả mãn một số tiêu chuẩn cho trước để làm mẫu.
Trong bài này, để trình bày vấn đề được đơn giản chúng ta đã thay mỗi điểm bằng một kí tự.
Bài tập làm thêm
           Bài T1. Với mỗi kí tự ccho trước hãy tìm trong tệp CNMAX.INP một hình chữ nhật tối đại chứa toàn kí tự c.
               Bài T2. Với cặp giá trị (k, c) cho trước hãy tìm hình chữ nhật tối đại chứa cùng một loại kí tự và đồng thời chứa điểm nằm trên dòng k, cột c của tệp CNMAX.INP.
 

Không có nhận xét nào:

Đăng nhận xét