Bài tốn đa giác

Một phần của tài liệu Tổ chức dữ liệu cho các thuật toán quay lui (Trang 29)

6. Ý nghĩa khoa học của đề tài

2.3. Bài tốn đa giác

2.3.1. Giới thiệu bài tốn

Năm 1967, tác giả Franz Owen Armbruster đã đƣa ra trị chơi cĩ tên Instant Insanity [6]. Trong vịng nhiều năm nĩ đã thu hút nhiều ngƣời chơi và trở nên rất phổ biến ở Mỹ.

Trị chơi Instant Insanity bao gồm bốn khối lập phƣơng với các mặt đƣợc tơ màu với bốn màu sắc (đỏ, xanh dƣơng, xanh lá cây và màu trắng).

Ngƣời chơi phải sắp xếp các khối trong một cột sao cho mỗi bên của cột (trƣớc, sau, trái và bên phải) cĩ màu của các khối khơng trùng nhau.

Thí dụ:

Hình 2.3.1 chƣa phải đáp án của bài tốn vì:

Ở mặt trên của cột, khối 1 và khối 4 cùng cĩ màu trắng: W Ở mặt dƣới của cột, khối 1 và khối 2 cùng cĩ màu đỏ: R

Hình 2.8 Trị chơi Instant Insanity

Tƣơng tự nhƣ trị chơi Instant Insanity, bài tốn đa giác đƣợc phát biểu nhƣ sau:

Cho m đa giác đều n cạnh đánh số từ 1 đến n theo chiều kim đồng hồ, tính từ cạnh trên cùng, mỗi cạnh sơn một trong số m màu.

Hãy chọn trên mỗi đa giác một cạnh làm điểm xuất phát để từ đĩ tính theo chiều kim đồng hồ ta nhận đƣợc đủ m màu khác nhau trên mỗi cạnh tƣơng ứng của m hình.

Xét thí dụ: Cho m = 4 hình lục giác (n = 6) và 4 màu X (Xanh), D (Đỏ), T (Tím) và V (Vàng) bố trí trên các cạnh nhƣ hình dƣới, cụ thể là:

Đa giác thứ nhất: V X T V D X. Đa giác thứ hai: X T T V X D. Đa giác thứ ba: T V D V D T. Đa giác thứ tƣ: X T D V D X.

Hình 2.10 Bài tốn đa giác với m = 4, n = 6

Ta tìm đƣợc đáp số 1, 5, 6, 3 với ý nghĩa sau đây:

Đa giác Cạnh xuất phát Liệt kê màu theo chiều kim đồng hồ

1 1 V X T V D X

2 5 X D X T T V

3 6 T T V D V D

Ta thấy trên cột nào cũng cĩ đủ 4 màu X D T V. Cĩ thể hiểu kết quả trên theo cách khác nhƣ sau.

Nếu chọn cạnh xuất phát của một đa giác là i thì cĩ thể quay đa giác đĩ ngƣợc chiều kim đồng hồ i - 1 lần, mỗi lần dịch chuyển một cạnh.

Khi đĩ đáp số 1, 5 , 6, 3 cho ta biết :

Giữ nguyên đa giác thứ nhất. Quay lần lƣợt các đa giác thứ hai 4 lần, đa giác thứ ba 5 lần và thứ tƣ 2 lần, sẽ thu đƣợc một cấu hình trong đĩ 4 cạnh tƣơng ứng của các đa giác đều tạo thành các tổ hợp X, D, T, V.

Hình 2.11 Đáp án của bài tốn đa giác với m = 4, n = 6

2.3.2. Tổ chức dữ liệu và chương trình

Phương án 1. Vét

(Xây dựng phƣơng án vét với m = 4, n = 6)

Ta mã số các màu : Xanh = 1, Đỏ = 2, Tím = 3 và Vàng = 4.

Sử dụng mảng kiểu nguyên c gồm n phần tử để đánh dấu các màu đã tơ trên mỗi cạnh các đa giác.

Sử dụng bảng a lƣu giá trị màu của các cạnh trên các đa giác, a[i][j] là màu của cạnh thứ j trên đa giác thứ i.

Gọi ij là cạnh xuất phát trên một đa giác, ij = 1, 2, 3, 4, 5, 6; j = 1, 2, 3, 4 . Theo phƣơng án này ta duyệt mọi tổ hợp chập 4 của 6 phần tử (i1, i2, i3, i4). Gặp tổ hợp đầu tiên thỏa yêu cầu của đầu bài thì ta lấy làm nghiệm.

Để giảm bớt các trƣờng hợp phải xét, ta giữ nguyên đa giác thứ nhất, tức là đặt i1 = 1, chỉ thay đổi các cạnh của 3 đa giác cịn lại.

int Vet(){

int i2, i3, i4;

for (i2 = 1; i2 <= n; ++i2) for (i3 = 1; i3 <= n; ++i3)

for (i4 = 1; i4 <= n; ++i4) if (Test(i2,i3,i4)) {

cout << "\n \n Phuong An 1: Vet";

cout << "\n Nghiem Bai Toan: 1 "<<i2<<" "<<i3<<" "<<i4; return 1; } cout << "\n Vo nghiem."; return 0; }

Để mỗi tổ hợp chập 4 dùng đủ 4 màu thì mỗi màu chỉ đƣợc xuất hiện đúng 1 lần.

Ta chọn các điểm xuất phát: i1 cho đa giác thứ nhất, i2 cho đa giác thứ hai, i3 cho đa giác thứ ba và i4 cho đa giác thứ tƣ rồi duyệt các cạnh theo vịng trịn, đánh dấu các màu đã tơ trên mỗi cạnh vào mảng c.

Nếu gặp một màu xuất hiện trên 1 lần thì coi nhƣ bỏ ứng viên đang xét. Hàm Test(i2,i3,i4) đƣợc triển khai nhƣ sau:

// vet can voi m = 4 mau = 4 dong int Test(int i2, int i3, int i4){ int j;

for (j = 1; j <= n; ++j){

memset(c,0,(m+1)*sizeof(int)); c[a[1][j]] = 1;

if (c[a[2][i2]] > 0) return 0; else c[a[2][i2]] = 1; if (c[a[3][i3]] > 0) return 0; else c[a[3][i3]] = 1;

if (c[a[4][i4]] > 0) return 0; else c[a[4][i4]] = 1; i2 = (i2 < n) ? (i2 + 1) : 1; i3 = (i3 < n) ? (i3 + 1) : 1; i4 = (i4 < n) ? (i4 + 1) : 1; } return 1; }

Phương án 2. Quay lui

Gọi id[1..k] là dãy các điểm đầu thu đƣợc khi giải bài tốn với k đa giác

n cạnh, mỗi cạnh đƣợc tơ một trong số màu từ 1 đến m.

Ta tạm gọi một dãy màu là dãy đơn k màu nếu dãy đĩ gồm k phần tử khác nhau đơi một.

Thí dụ, dãy (X, V, D, T), trong khi dãy (X, V, X, T) khơng phải là dãy đơn 4 màu vì cĩ chứa 2 màu xanh.

Điều kiện thỏa của nghiệm id[1..k] là: dãy màu trên các cạnh tƣơng ứng của các đa giác đều là dãy đơn k màu.

Với k = 1 thì id[1] chính là một nghiệm của bài tốn cho 1 đa giác vì mọi dãy màu đều là dãy đơn 1 màu.

Để tìm vị trí xuất phát id[k+1] cho đa giác thứ k+1 ta cần di chuyển cạnh xuất phát của đa giác này từ vị trí hiện hành về vị trí thứ n sao cho khi thêm màu của mỗi cạnh của đa giác này vào các dãy đơn k màu ta lại thu đƣợc các dãy đơn (k+1).

/*--- Tim tu vi tri id[k]+1..m tren dong k

de cac dong 1..k tao thanh cac cot khac nhau

---*/ int Find(int k) {

int v;

for (v = id[k]+1; v <= n; ++v) if (Diff(k,v) > 0) return v; return 0;

}

Nếu tìm đƣợc một cạnh nhƣ vậy ta cập nhật giá trị cho id[k+1] và tiếp tục xét đa giác tiếp theo.

void Tien(int &k, int v){ int i;

id[k] = v; Fill(k,v); ++k;

}

Ngƣợc lại, khi khơng tìm đƣợc một cạnh xuất phát hợp lệ cho đa giác thứ (k+1) ta quay lui lại đa giác thứ k để chỉnh lại cạnh xuất phát của đa giác này.

void Lui(int &k, int v){ int i;

Clear(k,v); id[k] = 0; --k; }

Hàm Diff(k,v) duyệt từ cạnh thứ v của đa giác thứ k xem các màu đã xuất hiện trên cạnh tƣơng ứng của các đa giác 1 đến k - 1 hay chƣa. Hàm cho ra trị v nếu chƣa xuất hiện màu trùng lặp, ngƣợc lại, hàm cho ra trị 0.

int Diff(int k, int v) { int i, res = v;

for (i = 1; i <= n; ++i, v = (v < n) ? v+1 : 1) if (cc[a[k][v]][i] > 0) return 0;

return res; }

Ta sử dụng một bảng cc để đánh dấu những màu trên các cạnh của các đa giác đã duyệt, cc[i][j] = 1 cho biết màu i đã xuất hiện tại cạnh thứ j của đa giác, ngƣợc lại, nếu màu i này chƣa xuất hiện trên cạnh thứ j thì ta phải cĩ cc[i][j] = 0. Nhƣ vậy mỗi cột j của bảng cc sẽ đánh dấu một tổ hợp màu trên cạnh thứ j của các đa giác.

Vì ta duyệt các cạnh của đa giác theo vịng trịn nên từ cạnh thứ v

chuyển qua cạnh tiếp theo sẽ đƣợc tính nhƣ sau v = (v < n) ? v + 1 : 1; Với thí dụ đã cho, ta cĩ :

Giữ nguyên đa giác thứ nhất. Dùng bảng cc[i][j] để đánh dấu màu trên các cạnh của đa giác.

Bảng cc[i][j]: 1 2 3 4 1 1 2 1 3 1 4 1 5 1 6 1

Duyệt đa giác thứ 2. Dãy id = {1,1} (Cạnh xuất phát của đa giác thứ nhất là 1, cạnh xuất phát của đa giác thứ 2 là 1).

Điền màu trên các cạnh của đa giác thứ 2 vào Bảng cc[i][j] :

1 2 3 4 1 1 1 2 1 1 3 1 1 4 1 5 1 1 6 1 1

Khi duyệt đến i = 4 thì đã cĩ cc[4,4] = 1. Do đĩ, ta quay đa giác thứ 2 thêm 1 lần.

id = {1,2} (Cạnh xuất phát của đa giác thứ nhất là 1, cạnh xuất phát của đa giác thứ 2 là 2).

Điền màu trên các cạnh của đa giác thứ 2 vào Bảng cc[i][j]:

1 2 3 4 1 1 2 1 3 1 4 1 1 5 1 6 1

Khi duyệt i = 1 thì đã cĩ cc[1,6] = 1. Do đĩ, ta quay đa giác thứ 2 thêm 1 lần.

Tƣơng tự với trƣờng hợp id = {1,3} và id = {1,4} đều chƣa thỏa điều kiện, ta tiếp tục quay đa giác thứ 2 thêm 1 lần.

id = {1,5} Bảng cc[i][j]: 1 2 3 4 1 1 1 2 1 1 3 1 1 4 1 1 5 1 1 6 1 1

Duyệt hết các màu trên các cạnh của đa giác thứ hai, khơng cĩ cạnh nào cĩ màu trùng với cạnh tƣơng ứng của đa giác thứ nhất.

Cập nhật giá trị cho dãy id = {1,5} và chuyển sang xét đa giác thứ ba. Tiếp tục nhƣ vậy đến đa giác thứ ba và thứ tƣ ta thu đƣợc dãy

id = {1,5,6,3}. Bảng cc[i][j]: 1 2 3 4 1 1 1 1 1 2 1 1 1 1 3 1 1 1 1 4 1 1 1 1 5 1 1 1 1 6 1 1 1 1

Cách tìm ra dãy đáp số id = {1,5,6,3} cĩ thể diễn tả nhƣ sau:

Hình 2.12 Cách tìm nghiệm id = {1,5,6,3}

2.3.3. Nhận xét

Với phƣơng án Vét, giữ nguyên đa giác 1, trƣờng hợp xấu nhất ta phải xét tất cả 63 = 216 lần xoay các cạnh để tìm ra 1 nghiệm.

Với phƣơng án quay lui tổ chức thêm 1 bảng cc để đánh dấu màu các cạnh xuất phát trên mỗi đa giác, thời gian nhanh hơn phƣơng án Vét, giảm bớt đƣợc độ phức tạp, tuy nhiên tốn bộ nhớ hơn.

id 1,1 1,2 1,3 1,4 1,5,3 1,5 1,5,2 1,5,1 1,5,4 1,5,5 1,5,6 1,5,6,1 1,5,6,2 1,5,6,3

Một phần của tài liệu Tổ chức dữ liệu cho các thuật toán quay lui (Trang 29)

Tải bản đầy đủ (PDF)

(59 trang)