1. Trang chủ
  2. » Khoa Học Tự Nhiên

SKKN Ứng dụng thuật toán quay lui giải bài toán liệt kê

45 589 1

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

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 45
Dung lượng 361 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

SKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kêSKKN Ứng dụng thuật toán quay lui giải bài toán liệt kê

Trang 1

PHẦN I MỞ ĐẦU 1

1.1 Lý do chọn đề tài: 1

1.2 Mục đích, nhiệm vụ của việc thực hiện đề tài nghiên cứu: 1

1.3 Đối tượng, thời gian và phương pháp nghiên cứu: 2

1.3.1 Đối tượng nghiên cứu 2

1.3.2 Thời gian nghiên cứu 2

1.3.3 Phương pháp nghiên cứu 2

PHẦN II NỘI DUNG 3

2.1 Phương pháp (thuật toán quay lui Backtracking) 3

2.2 Dạng 1: Tìm tất cả các nghiệm 4

2.3 Dạng 2 : Tìm một nghiệm 21

2.3 Dạng 3 Tìm nghiệm tối ưu thoả mãn điều kiện 29

2.3.1 Nghiệm tối ưu 29

2.3.2 Kỹ thuật nhánh cận 30

PHẦN III KẾT LUẬN VÀ KIẾN NGHỊ 40

3.1 KẾT LUẬN 40

3.2 KIẾN NGHỊ 41

Trang 2

PHẦN I

MỞ ĐẦU1.1 Lý do chọn đề tài:

Với mỗi bài toán trong Tin học thường có rất nhiều phương pháp giải, nhưngtìm được phương pháp giải tối ưu không phải là vấn đề đơn giản Để giải một bàitoán thường phải xác định được:

- Bài toán thuộc lớp bài toán nào

- Sử dụng phương pháp tối ưu nào để giải nó

Có những bài toán yêu cầu phải liệt kê nghiệm theo một điều kiện nào đó Vớilớp bài toán này sử dụng thuật toán quay lui để giải quyết sẽ dễ dàng và đơn giản hơn

các phương pháp khác (phương pháp “sinh” cũng giải được một số bài toán liệt kê cấu hình) Vì vậy, tôi đề xuất sáng kiến “Ứng dụng thuật toán quay lui giải bài toán liệt kê” Tuy “thuật toán quay lui” là không mới, không tối ưu trong việc giải

quyết một số bài toán nào đó mà phương pháp khác cũng giải được, nhưng cũng cónhiều bài toán mà chỉ có thuật toán này mới giải quyết được nó một cách dễ dàng.Những bài toán dùng phương pháp quay lui để giải gặp rất nhiều trong các kỳ thi họcsinh giỏi tỉnh, Tin học trẻ không chuyên cũng như học sinh giỏi quốc gia trong nhiềunăm

Ý tưởng cơ bản của “thuật toán quay lui” là liệt kê hết tất cả các khả năng có

thể, hay còn gọi là phương pháp vét cạn, duyệt hết cấu hình

1.2 Mục đích, nhiệm vụ của việc thực hiện đề tài nghiên cứu:

Nhằm giúp giáo viên và học sinh khi đứng trước một bài toán, xác định được

là bài toán đó có thể áp dụng được thuật toán quay lui hay không? Và cách giải cụthể như thế nào? Từ đó tôi đề ra mục đích, nhiệm vụ của việc thực hiện đề tài nhưsau:

- Giới thiệu khái niệm “thuật toán quay lui (Backtracking)”, những ưu điểm,hạn chế của thuật toán

- Giới thiệu một số bài toán điển hình trong lớp bài toán

Vì thế sáng kiến có các nội dung sau:

Trang 3

Mục 1 Phương pháp quay lui (backtracking).

Mục 2 Dạng 1: Tìm tất cả các nghiệm

Mục 3 Dạng 2: Tìm một nghiệm

Mục 4 Dạng 3: Tìm nghiệm tối ưu thoả mãn điều kiện

Với mỗi dạng có các ví dụ điển hình, ý tưởng và chương trình tham khảo

1.3 Đối tượng, thời gian và phương pháp nghiên cứu:

1.3.1 Đối tượng nghiên cứu:

Sáng kiến kinh nghiệm “Ứng dụng thuật toán quay lui giải bài toán liệt kê” có

đối tượng nghiên cứu là các bài toán giải bằng thuật toán quay lui

1.3.2 Thời gian nghiên cứu:

“Thuật toán quay lui” được nghiên cứu trong quá trình học tập và giảng dạy,bồi dưỡng các đội tuyển học sinh giỏi

1.3.3 Phương pháp nghiên cứu:

Chủ yếu là nghiên cứu đề tài, tham khảo tài liệu, ý kiến đóng góp của đồng nghiệp và qua thực tiễn giảng dạy

Trang 4

PHẦN II NỘI DUNG 2.1 Phương pháp (thuật toán quay lui Backtracking)

Trong các kỹ thuật cơ bản để thiết kế thuật toán, quay lui là một trong những kỹthuật quan trọng nhất vì nó cho phép giải một lớp các bài toán khá lớn có dạng tổngquát như sau:

Tìm một (hoặc tất cả) bộ nghiệm (x1, x2, , xn) thỏa mãn điều kiện F nào đó,trong đó các thành phần xi được chọn từ một tập hữu hạn Di với mọi i = 1, 2, , n

Tư tưởng của phương pháp quay lui như sau :

- Ta xây dựng bộ nghiệm từng bước, bắt đầu từ thành phần nghiệm x1 đượcchọn trong các giá trị có thể S1 = D1

- Giả sử đã chọn được các thành phần x1, x2, , xi-1, từ các điều kiện của bàitoán ta sẽ xác định được tập Si gồm các giá trị được chọn cho thành phần nghiệm xi.Tập Si là tập con của Di và phụ thuộc vào các thành phần x1, x2, , xi-1 đã chọn Chọnmột phần tử xi thuộc Si như một thành phần của bộ nghiệm Từ bộ (x1, x2, , xi) lặplại quá trình trên để tiếp tục mở rộng nghiệm cho thành phần xi+1 Nếu không chọnđược thành phần nào của xi+1 (do Si+1 rỗng) thì ta quay lại chọn một phần tử khác của

Si làm thành phần nghiệm xi (ý nghĩa của quay lui là ở bước này)

- Trong quá trình mở rộng nghiệm ta luôn kiểm tra nghiệm thành phần có phải

là nghiệm của bài toán hay không Nếu chỉ cần một nghiệm thì ta dừng lại, nếu cầntìm tất cả các nghiệm thì quá trình tìm nghiệm chỉ dừng khi tất cả các khả năng chọncác thành phần nghiệm đã vét cạn

Quay lui là một trong các phương pháp vét cạn trong đó các giá trị nghiệm đượcchọn không thực hiện được bằng cách duyệt tuyến tính Điểm tốt của thuật toán quaylui so với vét cạn tuyến tính là hạn chế bớt các nhánh phải duyệt mà theo nhữngnhánh đó không tìm được lời giải thể hiện ở việc xây dựng các tập giá trị có thể Si

khi tìm thành phần nghiệm xi và quay lui khi không mở rộng thành phần nghiệm xi+1 Tuy nhiên hạn chế của phương pháp này là phải duyệt qua nhiều khả năng nên độphức tạp của chương trình thường ở mức giai thừa hay hàm mũ nên tốc độ tính toán

Trang 5

khá lâu trong trường hợp kích thước của dữ liệu vào khá lớn Để khắc phục hạn chếnày người ta tìm cách hạn chế các khả năng không đưa đến kết quả bằng phươngpháp nhánh cận, tuy nhiên lớp bài toán dùng được phương pháp này không nhiều.

Đa số các bài toán vét cạn sử dụng phương pháp duyệt đệ quy để xét hết mọi khảnăng có thể có nghiệm theo nguyên tắc "thử sai" và quay lui Về tư tưởng, các thuậttoán vét cạn rất đơn giản nhưng ứng dụng vào việc giải các bài toán cần có những kỹthuật nhất định

Để giải bài toán bằng thuật toán quay lui cần thực hiện các công việc quan

* Tìm các điều kiện để các nghiệm đã chọn là các nghiệm của bài toán

Một số lưu ý khi xây dựng thuật toán quay lui:

* Phải duyệt qua mọi phương án của bài toán có thể chứa nghiệm (vét cạn)

* Tránh trường hợp duyệt trùng lặp các khả năng đã duyệt

Để giải các bài toán bằng thuật toán quay lui, thông thường ta thường dùng thủtục đệ quy Try(i : Integer) để chọn thành phần nghiệm xi

Có ba dạng cơ bản trong các thuật toán quay lui:

Dạng 1: Tìm tất cả các nghiệm.

Dạng 2: Tìm một nghiệm.

Dạng 3: Tìm nghiệm tối ưu thỏa mãn điều kiện.

Ta lần lượt xét cách giải các dạng trên bằng phương pháp quay lui:

2.2 Dạng 1: Tìm tất cả các nghiệm

Mô hình của thuật toán quay lui có thể mô tả như sau:

Procedure Try(i: Integer); {Thủ tục này thử cho xi nhận lần lượt các giá trị mà nó có thể nhận}

Begin

for <mọi giá trị j có thể gán cho xi> do

Trang 6

<Thử cho xi := j>;

If <xi là phần tử cuối cùng trong cấu hình> then

<Thông báo cấu hình tìm được>

Else

Begin

<Ghi nhận việc cho xi nhận giá trị j (Nếu cần)>;

Try(i + 1); {Gọi đệ quy để chọn tiếp xi+1}

<Nếu cần, bỏ ghi nhận việc thử xi := j, để thử giá trị khác>;

VÍ DỤ 1 LIỆT KÊ DÃY NHỊ PHÂN ĐỘ DÀI N

Input: file văn bản NHIPHAN.INP chứa số nguyên dương N (N<=30).

Output: file văn bản NHIPHAN.OUT ghi các dãy nhị phân, mỗi dãy trên một dòng.

Ý tưởng:

Biểu diễn dãy nhị phân độ dài N dưới dạng (x1,

x2, ., xn) Ta sẽ liệt kê các dãy này bằng cách thử

dùng các giá trị {0, 1} gán cho xi Với mỗi giá trị thử

gán cho xi lại thử các giá trị có thể gán cho xi+1…

Chương trình liệt kê bằng thuật toán quay lui có thể

viết:

Trang 7

If i = n then Incauhinh {Nếu i = n thì Incauhinh}

Else Try(i + 1); {Nếu i chưa phải là phần tử cuối thì tìm tiếp x i+1 }

Trang 8

Nghiệm của bài toán tìm các tổ hợp chập k của n phải thoả mãn các điều kiệnsau:

- Là một vectơ X= (x1, x2,…, xk);

- Xi lấy giá trị trong tập {1,2,3,…,n};

- Ràng buộc: xi < xi+1 với mọi giá trị i từ 1 đến k-1 (vì tập hợp không phân biệtthứ tự phần tử nên ta sắp xếp các phần tử theo thứ tự tăng dần)

Ta có: 1x1x2<…< xkn, do đó xi được chọn là xi-1 + 1 xi  n - k + i(1  i  k) ở đây ta giả thiết có thêm một số x0 = 0 khi xét i = 1 Như vậy ta sẽ xéttất cả các cách chọn x1 từ 1 (= x0 + 1) đến n - k + 1, với mỗi giá trị đó, xét tiếp tất cả{1,2},{1,3},{1,4},{1,5},{2,3}, {2,4}, {2,5}, {3,4}, {3,5},{4,5};

Trang 9

các cách chọn x2 từ x1 + 1 đến n - k + 2, cứ như vậy khi chọn được đến xk thì ta

có một cấu hình cần liệt kê

Trang 10

Assign(g, 'TOHOP.INP'); Reset(g);

Assign(f, 'TOHOP.OUT'); ReWrite(f);

Readln(g,n, k);

x[0] := 0; Try(1); Close(g); Close(f);

End

{============================}

VÍ DỤ 3 LIỆT KÊ CÁC CHỈNH HỢP KHÔNG LẶP CHẬP K

Input: file văn bản CHINHHOP.INP chứa hai số nguyên dương n, k (1 kn

đưa về liệt kê các cấu hình (x1, x2, , xk),

ở đây các xi  S và khác nhau đôi một

Nghiệm của bài toán tìm các chỉnh

hợp không lặp chập k của tập n số nguyên từ 1 đến n là vectơ X thoả mãn điềukiện:

- Là một vectơ X = (x1, x2,…, xk);

- Xi lấy giá trị trong tập {1,2,3,…,n};

- Ràng buộc: các giá trị xi đôi một khác nhau, tức là xi ≠ xj với mọi i≠ j

Trang 11

Như vậy thủ tục Try(i) - xét tất cả các khả năng chọn xi - sẽ thử hết các giá trị từ

1 đến n, mà các giá trị này chưa bị các phần tử đứng trước chọn Muốn xem cácgiá trị nào chưa được chọn ta sử dụng kỹ thuật dùng mảng đánh dấu:

- Khởi tạo một mảng c1, c2, , cn kiểu logic Ở đây ci cho biết giá trị i có còn

tự do hay đã bị chọn rồi Ban đầu khởi tạo tất cả các phần tử mảng c là TRUE cónghĩa là các phần tử từ 1 đến n đều tự do

Trang 12

If c[j] then {Chỉ xét những giá trị j còn tự do}

Assign(g, 'CHINHHOP.INP'); Reset(g);

Assign(f, 'CHINHHOP.OUT'); ReWrite(f);

Readln(g,n,k);

FillChar(c, SizeOf(c), True); {Tất cả các số đều chưa bị chọn}

Try(1); {Thử các cách chọn giá trị của x 1 }

Close(g); Close(f);

End

{==========================}

Nhận xét: khi k = n thì đây là chương trình liệt kê hoán vị.

VÍ DỤ 4 BÀI TOÁN PHÂN TÍCH SỐ

Hãy tìm tất cả các cách phân tích số n thành tổng của các số nguyên dương,các cách phân tích là hoán vị của nhau chỉ tính là 1 cách

Input: file văn bản PHANTICH.INP chứa số nguyên dương n (n 30).

Output: file văn bản PHANTICH.OUT ghi các cách Ý tưởng số n.

Trang 13

Khi liệt kê các dãy x có tổng các

phần tử đúng bằng n, để tránh sự trùng lặp ta đưa thêm ràng buộc xi-1  xi Khi ti =

Trang 15

VÍ DỤ 5 BÀI TOÁN XẾP N QUÂN HẬU

Xét bàn cờ tổng quát kích thước n x n Một quân hậu trên bàn cờ có thể ănđược các quân khác nằm tại các ô cùng hàng, cùng cột hoặc cùng đường chéo.Hãy tìm cách xếp n quân hậu trên bàn cờ sao cho không quân nào ăn quân nào

Input: file văn bản QUEENS.INP chứa số nguyên dương n  12.

Output: file văn bản QUEENS.OUT, mỗi dòng ghi một cách đặt n quân hậu.

tìm ra được vị trí cột của những quân hậu.

Trang 16

- Mảng c[1 - n n – 1] để đánh dấu đường chéo còn lại.

- Ban đầu cả 3 mảng đánh dấu đều mang giá trị TRUE (Các cột và đường chéo đều

tự do), nếu phần tử nào được chọn thì cả 3 đường chéo nhận giá trị bằng FALSE

* Khi chọn vị trí cột j cho quân hậu thứ i, thì ta phải chọn ô (i, j) không bị các quânhậu đặt trước đó ăn, tức là phải chọn cột j còn tự do Điều này có thể kiểm tra (aj

= bi+j = ci-j = TRUE)

b: array[2 2 * max] of Boolean;

c: array[1 - max max - 1] of Boolean;

{============================}

Procedure khoitao;

Begin

Readln(f,n);

FillChar(a, SizeOf(a), True);

FillChar(b, SizeOf(b), True);

FillChar(c, SizeOf(c), True);

Trang 17

for i := 1 to n do Write(f,'(', i, ', ', x[i], '); '); Writeln(f);

Trang 18

Có N người (đánh số từ 1 đến N) tham gia một đợt xổ số điện toán Mỗi ngườinhận được một thẻ gồm M ô (đánh số từ 1 đến M) Người chơi được chọn K ô trong

số các ô đã cho bằng cách đánh dấu các ô được chọn Sau đó các thẻ này được đưavào máy tính để xử lý Máy tính chọn ra K ô ngẫu nhiên (gọi là các ô kết quả) vàchấm điểm từng thẻ dựa vào kết quả đã sinh Cứ mỗi ô chọn đúng với ô kết quả thìthẻ chơi được tính 1 điểm Giả thiết biết các ô chọn cũng như các điểm tương ứngcủa từng thẻ chơi, hãy xác định tất cả các kết quả có thể có mà máy sinh ra

INPUT: Dữ liệu vào đọc từ file văn bản XOSO.INP gồm:

- Dòng đầu ghi các số N, M, K

- Dòng thứ i trong N dòng tiếp ghi thẻ chơi của người i gồm K+1 số: K số đầu là các số hiệu của các ô chọn, cuối cùng là điểm tương ứng

OUPUT: Ghi kết quả ra file văn bản XOSO.OUT, mỗi dòng là một kết quả gồm K

số ghi số hiệu các ô mà máy đã sinh

Trang 19

chập K của M phần tử Ta áp dụng thuật toán quay lui để duyệt mọi cấu hình tổ hợp

để tìm ra cấu hình thoả mãn Tuy nhiên để giảm bớt số lần duyệt ta cần phải loạinhững thẻ mà chúng có tổng điểm bằng 0 và cần đánh dấu những thẻ đã được chọn

Dùng mảng kt[0 51] of Boolean để phân biệt giữa ô có điểm và những ôkhông có điểm

Nếu kt[i]= False thì cho biết thẻ thứ i không có điểm

Còn logic[i,j] = True cho ta biết người thứ i đánh dấu vào thứ j của thẻ

Trang 20

{đánh dấu tất cả các dòng có điểm, nếu dòng nào có điểm thì chấp nhận j= True;}

Function Chapnhan(j: Byte): Boolean;

Trang 22

<Thông báo cấu hình tìm được>

Halt; {thoát khỏi chương trình}

End Else

Trang 23

<Ghi nhận việc cho xi nhận giá trị j (Nếu cần)>;

Try(i + 1); {Gọi đệ quy để chọn tiếp xi+1}

<Nếu cần, bỏ ghi nhận việc thử xi := j, để thử giá trị khác>;

+ Điều kiện cần để một khả năng được chấp nhận ở bước thứ i là bước i+1 cũng

có khả năng chấp nhận một nghiệm của nó và bước thứ i chưa phải bước cuối cùng

Vì vậy có thể nhanh chóng tới đích nếu đưa ra qui luật chọn nghiệm của bước thứ inhư sau :

Ở bước thứ i ta sẽ chọn nghiệm nào mà theo nó đưa ta tới bước i+1 có ít khảnăng chấp nhận nhất (nghĩa là bước thứ i+1 vẫn có khả năng chọn nghiệm của nó,nhưng số nghiệm ít)

+ Một cách khác: Khi chấp nhận một khả năng chọn nghiệm cho bước thứ i, cóthể sẽ tác động tới trạng thái bài toán Vì vậy ta tính toán trước nếu chọn nghiệm nàythì trạng thái bài toán có thay đổi quá mức giới hạn cho phép hay không? Nghĩa là cóvượt qua cận trên hoặc cận dưới của bài toán hay không ? Nếu vượt qua thì ta khôngchọn nghiệm ấy Trong nhiều bài toán những cận này cũng thu hẹp dần theo từngbước, nếu ta tìm được sự thay đổi của cận theo từng bước thì các khả năng chọnnghiệm ngày càng hẹp dần, bài toán nhanh chóng kết thúc

Một khó khăn khác của loại toán hiện 1 nghiệm là: trường hợp bài toán vônghiệm cần viết chương trình như thế nào? Phải duyệt hết mọi khả năng mới rõ kếtluận vô nghiệm hay không vô nghiệm Nghĩa là đã đi theo mọi nhánh nhưng nhánhnào cũng đều không tới đích, do đó theo quy luật cứ quay lui mãi để tìm kiếm thì đếnlúc nào đó dẫn đến tình trạng phải trở về ô xuất phát Vậy khi gặp ô chọn nghiệm

Trang 24

mới trùng với ô xuất phát thì bài toán vô nghiệm Vậy trong thủ tục Try(i) ta thêmmột bước kiểm tra xem chương trình có nghiệm hay không nữa

Trang 25

VÍ DỤ 1 TÌM ĐƯỜNG ĐI TRONG MÊ CUNG

Mê cung gồm N phòng (N<100) có các hành lang nối với nhau đó là nơi trúngụ của quái vật Minotau (Nửa bò, nửa người) Ban ngày quái vật thường ra khỏi mêcung phun lửa giết chóc tàn phá với sức mạnh không ai địch nổi Ban đêm quái vậtngủ trong mê cung và hòn than lửa của nó được cất ở phòng “Dich”; ai lấy được hònthan lửa ấy thì chinh phục được quái vật Theo lời thỉnh cầu của công chúa Arian,anh hùng Têđê nhận lời sẽ vào mê cung thu phục quái vật Têđê xuất phát từ phòng

XP và quyết định dùng thuật toán tìm kiếm bằng vét cạn và quay lui (cùng cuộn chỉcủa nàng Arian tặng chàng để quay lui thuận tiện) Trong mê cung tối om dày đặcphòng và hành lang, chàng đã tìm được được phòng “Dich” và thu phục quái vật

Hãy lập trình hiện đường đi của Têđê

Input: File ‘MECUNG.TXT’ tổ chức như sau:

+ Dòng đầu là 3 số N XP Dich

+ N dòng tiếp theo: Dòng thứ i: Đầu tiên là số i (1≤ i ≤ N) tiếp theo là các số j (hai

số liền nhau cách nhau ít nhất 1 khoảng trống) thể hiện có hành lang một chiều từphòng i sang phòng j

Output: Đường đi của Têđê: liệt kê lần lượt các phòng chàng sẽ đi qua (không kể

những đoạn phải quay lại)

Ý tưởng:

Mảng A thể hiện các đường đi một chiều từ phòng i đến phòng j

Mảng T dùng để tính tổng số đường đi từ phòng xuất phát đến phòng i

Mảng D để đánh dấu xem phòng i đã được đi qua hay chưa

Mảng KQ để lưu lại đường đi

Ngày đăng: 27/10/2017, 23:39

HÌNH ẢNH LIÊN QUAN

3) Nhánh cận: Khởi tạo cấu hình CAUHINH cĩ chi phí = +∞. Với mỗi bước thử chọn xi   xem chi phí đường đi cho tới lúc đĩ cĩ &lt; Chi phí của cấu hình CAUHINH ? nếu khơng nhỏ hơn thì thử giá trị khác ngay bởi cĩ đi tiếp cũng chỉ tốn thêm - SKKN Ứng dụng thuật toán quay lui giải bài toán liệt kê
3 Nhánh cận: Khởi tạo cấu hình CAUHINH cĩ chi phí = +∞. Với mỗi bước thử chọn xi xem chi phí đường đi cho tới lúc đĩ cĩ &lt; Chi phí của cấu hình CAUHINH ? nếu khơng nhỏ hơn thì thử giá trị khác ngay bởi cĩ đi tiếp cũng chỉ tốn thêm (Trang 35)

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w