Bài toán Nonogram
Bài viết được đăng lại từ đây: https://hackmd.io/@alexisparis/itknowledge2
Đặt vấn đề

Bài viết này dự kiến sẽ được hoàn thành vào dịp cận Halloween (hoặc là run rủi sẽ rơi vào đúng hôm Halloween, không chắc nữa), vậy nên trước hết tôi xin dành một lời chúc đến các bạn đọc một mùa Halloween vui vẻ (đặc biệt là các bạn trẻ đang trải nghiệm trực tiếp cảm giác cầm lồng đèn Jack O'Lantern và đi gõ cửa các nhà tại các nước Âu Mỹ, hoặc là những người được gõ cửa và chào đón bằng câu "Trick or Treat"/"Un bonbon ou Un sort").
Nếu các bạn còn nhớ về chuỗi những quảng cáo trò chơi rác đã từng gặp qua, đỉnh điểm là vào năm 2023 (chính xác là tôi gặp nó rất thường xuyên và ở IP trong nước Pháp), các bạn sẽ hình dung được cách tôi tạo ra bức hình bên trên. Với các hàng và cột gắn liền với một dãy số định sẵn, chúng ta sẽ tô đen các ô trong một bức khung trắng, sao cho số lượng và bố cục của ô đen ở mỗi hàng và cột thỏa mãn các dãy số gắn liền với chúng (và phải đảm bảo rằng nó đã được chuẩn bị một cách logic nhất).
Loại puzzle trên có rất nhiều tên gọi, từ Picross, Logigraphe, Hanjie, Griddler, Nonogram, Logimage, hay gắn với đồ án năm 3 (năm cuối) Đại học của tôi với cách gọi Tomographie discrète. Theo những gì tôi lượm vu vơ trên Wikipedia tiếng Anh, nó có liên quan đến giải thưởng của biên tập viên đồ họa người Nhật Bản Non Ishida về việc tạo ra một bức tranh lưới từ các hệ thống ánh sáng tòa nhà chọc trời, và trực tiếp khai sinh bởi nhà thiết kế puzzle đồng hương Tetsuya Nishio trong cùng khoảng thời gian. Đồ án của tôi, cũng như của sinh viên năm 3 Cử nhân tại Đại học Sorbonne niên khóa 2023-2024, chính là tạo ra một thuật toán giải quyết các puzzle như thế trên cơ sở một file .txt cung cấp sẵn các dãy số tương ứng với hàng và cột.
Ở đây tôi sẽ gọi nó là Nonogram - một cách gọi quen mồm từ những ngày đầu mày mò thực hiện bài lập trình, bắt nguồn từ tên ứng dụng game tôi đã tải về để chơi liên tục trong một tuần đầu. Trong bài này, tôi sẽ xen kẽ hai phương pháp giải vấn đề: Phương pháp tối ưu (Optimal method) và Phương pháp gắn với bản năng người chơi (Instinct-based method).
Tiếp cận bài toán
Giải quyết một chuỗi ô
Đối với một chuỗi ô, có thể là một hàng hoặc một cột của khung trò chơi, vấn đề đặt ra cho mỗi ô là nên chọn tô màu đen hay màu trắng. Việc này được thực hiện lên trên hai trường hợp khái quát: một chuỗi ô trống không và một chuỗi ô đã được tô màu một ít.
Chúng ta mô hình hóa vấn đề trên bằng giải thuật đệ quy nhị nguyên dưới đây:
Ta gọi là chuỗi ô tô màu theo dãy số tương ứng , với:
- tương ứng với ô chưa được tô.
- tương ứng với ô được tô trắng.
- tương ứng với ô được tô đen.
Gọi là chỉ mục định vị dãy số .
Gọi là chỉ mục định vị chuỗi ô .
Hàm sẽ trả ra giá trị đúng () và sai () dựa trên chuỗi.
Chúng ta sẽ bắt đầu giải thuật với giá trị lớn nhất của các chỉ mục:
Bước cơ sở:
: Khi mọi giá trị trong dãy số đã được tuân thủ. Chúng ta liệt kê hai trường hợp dưới đây:
- : Không còn ô nào phía trước ô hiện tại :
- : Còn một cơ số ô ở phía trước ô hiện tại :
- Nếu tồn tại bất kì ô () đã được tô đen, vậy có nghĩa là các ô trong chuỗi đã bị tô không hợp lệ:
- Nếu không, chuỗi ô được mặc nhiên tô đúng:
: Số ô của chuỗi còn lại được xem xét nhỏ hơn với giá trị ở thứ tự trong dãy số. Không nghi ngờ gì, chuỗi được tô không hợp lệ:
- : Số ô của chuỗi còn lại được xem xét bằng đúng với giá trị ở thứ tự trong dãy số Chúng ta liệt kê hai trường hợp dưới đây:
- : Giá trị được so sánh của dãy số là giá trị ở hàng đầu Chúng ta sẽ xem xét trong ô còn lại như sau:
- Nếu trong số đó có ô đã được tô trắng, vậy nghĩa là chuỗi đang không được tô hợp lệ:
- Nếu không, chuỗi ô vẫn được xác định là tô hợp lệ:
- : Giá trị được so sánh của dãy số không phải là giá trị ở hàng đầu Không nghi ngờ gì, chuỗi đã bị tô không hợp lệ:
Bước đệ quy
- Nếu ô đã được tô trắng, công việc tiếp theo của ta là đệ quy để kiểm tra các ô phía trước với cùng giá trị trong dãy số:
Nếu ô đã được tô đen, chúng ta xem xét chuỗi ô phía trước, trên giả định ô là ô phía cùng bên phải trong chuỗi ô đen. Để diễn giải cho dễ hiểu, ta xem xét nhóm ô từ đến .
- Nếu ô đã được tô đen, điều này có nghĩa là trong mọi trường hợp, ô không thể được tô đen vì sẽ không thể tạo ra chuỗi thỏa mãn giá trị :
- Trong chuỗi ô từ đến , điều này có nghĩa là không thể tô đen các ô kế tiếp để thỏa mãn việc ô là ô cuối chuỗi:
- Nếu không phạm phải bất kỳ quy tắc nào phía trên, chúng ta sẽ đệ quy kiểm tra tiếp các ô trước đó cùng với giá trị phía trước trong dãy số:
Nếu ô chưa được tô, ta có hai hàm và tương ứng với hai trường hợp 1 và 2 của bước đệ quy:
Tóm lại, giá trị của theo và tương ứng:
Độ khó của chuỗi ô
Hàm nêu trên có tác dụng xác định việc tô màu chuỗi ô có hợp lệ hay không. Tuy nhiên, một chuỗi ô có thể có nhiều cách tô khác nhau cùng thỏa mãn một dãy số.
Ta hãy xem xét các ví dụ dưới đây về việc tô màu theo các dãy số lên trên một chuỗi gồm 11 ô vuông ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜.
Ví dụ 1:
Chúng ta có các cách tô như sau:
- Cách 1:

- Cách 2:

- Cách 3:

- Cách 4:

Ví dụ 2:
Chúng ta có các cách tô như sau:
- Cách 1:

- Cách 2:

- Cách 3:

- Cách 4:

- Cách 5:

- Cách 6:

Ví dụ 3:
Chúng ta có các cách tô như sau:
- Cách 1:

- Cách 2:

- Cách 3:

- Cách 4:

- Cách 5:

- Cách 6:

- Cách 7:

- Cách 8:

- Cách 9:

- Cách 9:

Ví dụ 4:
Chúng ta có các cách tô như sau:
- Cách 1:

- Cách 2:

- Cách 3:

- Cách 4:

- Cách 5:

- Cách 6:

- Cách 7:

- Cách 8:

- Cách 9:

Ví dụ 5:
Chúng ta có các cách tô như sau:
- Cách 1:

- Cách 2:

- Cách 3:

- Cách 4:

- Cách 5:

- Cách 6:

- Cách 7:

- Cách 8:

- Cách 9:

- Cách 10:

- Cách 11:

- Cách 12:

- Cách 13:

- Cách 14:

- Cách 15:

- Cách 16:

- Cách 17:

- Cách 18:

- Cách 19:

- Cách 20:

- Cách 21:

Ví dụ 6: Một số trường hợp cụ thể chỉ có một cách tô
-
:

-
:

-
:

-
:

-
:

Thông qua các ví dụ trên, chúng ta có thể đưa ra các kết luận như sau: Độ khó của chuỗi ô phụ thuộc vào tổng các giá trị trong dãy số và độ dài của dãy.
Trong trường hợp dãy số trống () hoặc dãy số một giá trị duy nhất () tương ứng với độ dài của chuỗi (), ta chỉ có duy nhất một cách tô chuỗi.
Nếu độ dài và các giá trị của dãy đảm bảo được đầu và cuối chuỗi được tô đen và giữa các chuỗi con chỉ cần một ô tô trắng để phân cách, thì chuỗi cũng chỉ có duy nhất một cách tô.
Đối với dãy số chỉ có một giá trị, số cách tô phụ thuộc vào độ lớn của giá trị ấy. Khi độ lớn của giá trị càng nhỏ, số cách tô màu chuỗi càng tăng.
Đối với dãy số có từ hai giá trị trở lên, ta đặt .
Số ô trắng tối thiểu để ngăn cách các chuỗi con được tô đen là .
Giá trị của càng nhỏ thì số cách tô màu chuỗi càng tăng.
Bổ đề: Bài toán chia kẹo Euler (Stars and Bars counting method)
Cho phương trình với tập nghiệm
Tập nghiệm có độ lớn là
Chứng minh
Gọi số nghiệm của phương trình . Mệnh đề đúng nếu .
-
Bước cơ sở: Nếu và , tổng số nghiệm của phương trình là . Mệnh đề đúng.
-
Bước quy nạp: Giả sử , bằng quy tắc chuyển vế chúng ta có: . Số nghiệm lúc này là . Vậy . Giả sử mệnh đề đúng, ta có: . Ta vì vậy có:
Bằng cách chứng minh quy nạp, ta kết luận mệnh đề thỏa mãn với mọi giá trị của và .
Định luật
Áp dụng bổ đề
Khi một chuỗi ô được tô màu theo dãy số, giả sử tồn tại trên chuỗi các "khe" được ngăn cách bởi các chuỗi con được tô đen, bao gồm 2 khe ở hai đầu chuỗi, và các khe ở giữa. Mỗi khe nằm ở giữa đều mặc định chứa một ô trắng ban đầu, các khe ngoài cùng được mặc định trống rỗng.
Bài toán được đặt ra: Làm sao xếp các ô trắng còn lại vào các khe.
Gọi là số khe, ta có .
Gọi là tổng số ô trắng "tự do", ta có với đã được nêu phía trên.
Gọi là số ô trắng nằm trong các khe, ta có phương trình .
Như vậy ta có định luật:
Đặt .
Đặt .
Số cách tô màu chuỗi thỏa mãn dãy số là
Hệ quả
Quan sát ví dụ 1, ta thấy năm ô từ đến đều được tô đen trong tất cả các cách tô.
Quan sát ví dụ 2, ta thấy bốn ô và đến đều được tô đen trong tất cả các cách tô.
Quan sát ví dụ 3, ta thấy duy nhất ô được tô đen trong tất cả các cách tô.
Quan sát ví dụ 4 và 5, không có ô nào được tô cố định màu qua các cách.
Ta lý giải vấn đề này như sau: Một ô được tô màu đen cố định khi không thể tô màu trắng trong bất kỳ cách tô nào.
Với mỗi chuỗi con tô theo dãy số, sự chênh lệch trong việc tô phụ thuộc vào số ô trắng "tự do". Nói cách khác, quay trở lại phần bổ đề trên, sự chênh lệch nằm ở việc tồn tại một khe có nhiều ô trắng "tự do" nhất được bỏ vào, trong khi các khe khác không có
Như vậy, tổng số ô được tô đen cố định là , từ đó một chuỗi con sẽ có các ô tô đen cố định khi và chỉ khi .
Một chuỗi ô có độ khó thấp nhất là chuỗi ô không có ô trắng "tự do", vì thế ta có hệ quả:
Chuỗi ô chỉ có 1 cách tô duy nhất để thỏa mãn dãy số khi và chỉ khi .
Xác định chỉ mục các ô tô đen cố định.
Để xác định vùng tô cố định của một chuỗi con trên chuỗi ô, ta có công thức:
Với là số ô tối thiểu được tô từ trái sang cho đến khi tô chuỗi .
Các thuật ngữ
Quy hoạch động
Quy hoạch động (Dynamic programming) được định nghĩa là chia vấn đề thành các vấn đề nhỏ, sao cho giữa các vấn đề nhỏ không có sự độc lập với nhau. Để thực thi mô típ này, một cấu trúc dữ liệu được sử dụng để lưu trữ các kết quả của vấn đề nhỏ, sao cho chúng được tái sử dụng ở các vấn đề nhỏ khác và cả những vấn đề lớn hơn.
Thông thường, các trang web tôi đọc qua đều sử dụng Fibonacci làm ví dụ cho việc áp dụng phương pháp trên:
Bằng phương pháp đệ quy truyền thống, ta có:
Hàm như sau:
- Nếu thì:
- Trả ra .
- Còn nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Trả ra
Kết thúc hàm
Sự đệ quy của hàm tạo ra một cây nhị phân với số nút đệ quy là .
Ở mỗi nút, phần phi đệ quy không có vòng lặp, nên độ phức tạp cục bộ là .
Vậy độ phức tạp chung của hàm là .
Ta có hai cách tiếp cận quy hoạch động sau đây
- Phương pháp top-down memoization (giải quyết từ trên xuống dưới và ghi nhớ thông qua phép đệ quy):
Gọi với giá trị khởi tạo của là , là và các giá trị khác được khởi tạo là . Hàm như sau:
- Nếu thì:
- Trả ra .
- Nếu không:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc điều kiện
- Trả ra .
Kết thúc hàm
- Phương pháp bottom-up tabulation (giải quyết từ dưới lên trên để làm cơ sở thông qua phép lặp):
Gọi với giá trị khởi tạo của giá trị là . Hàm như sau:
- Với mọi tiến hành:
- Nếu thì:
- .
- Còn nếu thì:
- .
- Nếu không:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Trả ra .
Kết thúc hàm
Đặc điểm của phương pháp quy hoạch động chính là mỗi bài toán con sẽ chỉ được tính toán đúng một lần. Vì thế độ phức tạp của hai hàm trên là .
Như vậy là phương pháp quy hoạch động rõ ràng tối ưu về mặt thời gian.
Vì vậy, giải thuật quy hoạch động thường được sử dụng trong các trường hợp sau:
- Các bài toán con "gối nhau": Khi kết quả từ bài toán con này hoàn toàn có thể tái sử dụng cho bài toán con kia.
- Tối ưu hóa cấu trúc con nhằm tối ưu hóa bài toán lớn: Thường thấy trong các bài toán lên kế hoạch và đồ thị.
Giải thuật tham lam
Giải thuật tham lam (Greedy algorithm) là giải thuật giải quyết một vấn đề dựa trên các lựa chọn đã được xếp hạng, phương pháp tối ưu nhất trong thời điểm sẽ được chọn và áp dụng rồi không được đề cập lại sau đó.
Một ví dụ cho dạng thuật toán này là thuật toán Kruskal, dùng để tìm cây khung nhỏ nhất trong đồ thị.
Đầu vào: Đồ thị vô hướng với là tập hợp các đỉnh, là tập hợp các cạnh và đo giá trị mỗi cạnh.
Đầu ra: Cây khung ngắn nhất xuất phát từ đồ thị .
- .
- .
- Với mọi tiến hành:
- Nếu chưa tồn tại con đường từ sang thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
Trong thuật toán nêu trên, các cạnh của đồ thị đã được sắp xếp theo thứ tự tăng dần của giá trị.
Giải quyết một chuỗi ô
Phương pháp 1
Vì ở tất cả các cách, có thể tồn tại các ô được tô đen cố định trong chuỗi. Vì vậy, phương pháp nguyên thủy nhất được sử dụng đến chính là "vét cạn" (brute force): Qua mỗi lần giải quyết chuỗi, chúng ta tổng hợp tất cả các cách tô màu để tìm ra các ô được tô cố định.
Ta có đoạn mã giả sau:
Hàm như sau:
- Nếu thì:
- Với mọi tiến hành:
- Nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Nếu không:
- Nếu thì:
- Trả ra .
- Còn nếu thì:
- Với mọi tiến hành:
- Nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Nếu không:
- Nếu thì:
- Trả ra .
- Còn nếu thì:
- Nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Với mọi tiến hành:
- Nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Trả ra .
- Nếu không:
- .
- .
- .
- .
- Nếu thì:
- Trả ra .
- Còn nếu thì:
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Còn nếu thì:
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Nếu không:
- Với mọi tiến hành:
- Nếu thì:
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Kết thúc điều kiện
- Kết thúc điều kiện
- Kết thúc điều kiện
- Kết thúc điều kiện
- Trả ra .
Kết thúc hàm
Nhìn tổng quan, thuật toán này được xây dựng trên nền tảng của hàm được đề cập ở đầu bài. Bên cạnh phương pháp xác định tô đúng, tô sai, ở mỗi công đoạn của hàm có thêm giai đoạn truy ngược (backtracking) để bổ sung và hoàn thiện.
Tính chất
Tính chất 1: Thuật toán chỉ tô các ô trống bên trong chuỗi , không đụng vào các ô có sẵn màu.
Chứng minh (Phản chứng):
Giả sử tồn tại ô nằm giữa và là ô được chỉnh sửa màu. Gọi là phiên bản cũ sao cho và .
Nếu thì hàm sẽ trực tiếp trả đệ quy ra .
Còn nếu thì xảy ra 2 trường hợp:
-
là ô cuối chuỗi con. Nếu là ô hợp lệ dựa trên các điều kiện phía trước thì hàm sẽ trả đệ quy về , nếu không sẽ tự động trả ra và kết thúc chương trình.
-
là ô phía trước chuỗi nhận () là ô cuối. Vòng lặp của sẽ bỏ qua .
Vì vậy, không có cách nào để trở thành . Điều này trái với giả thuyết ban đầu.
Tính chất 2: Thuật toán đảm bảo hoàn thiện các ô đen cố định bên trong chuỗi rỗng.
Chứng minh (Phản chứng):
Giả sử tồn tại ô nằm giữa và là ô đen cố định nhưng không được tô vào trong chuỗi rỗng. Vì là ô đen nên chắc chắn sẽ chỉ được tô khi .
Nếu nhận là ô đen cuối chuỗi, sẽ được tô đen qua vòng lặp truy ngược của .
Nếu là ô rỗng, vì là ô đen cố định nên sẽ , từ đó cũng sẽ được tô đen qua vòng lặp truy ngược.
Điều này chứng tỏ giả thiết trên không tồn tại.
Độ phức tạp
Trường hợp 1: Ở các bước cơ sở
Khi tiến hành chạy thuật toán, các bước cơ sở tiến hành các vòng lặp để kiểm tra:
- Các ô phía trước có hợp lệ chưa.
- Các ô phía trước có được tô đầy đủ không.
Độ phức tạp của bước cơ sở là .
Trường hợp 2: Ở các bước đệ quy
Khi , thuật toán trả đệ quy thẳng về . Độ phức tạp là .
Khi , thuật toán trả đệ quy về sau vòng lặp kiểm tra các ô phía trước. Vậy độ phức tạp là .
Khi , thuật toán sẽ chia hai trường hợp thông qua hai nút đệ quy song song rồi sử dụng vòng lặp truy ngược để tổng hợp. Vậy độ phức tạp là
Độ phức tạp của phương pháp 1 là .
Phương pháp 2
Đối với phương pháp này, chúng ta sẽ sử dụng nguyên bản hàm nhị nguyên ở đầu bài viết, thay vì cải biên lại như ở trong phương pháp 1.
Ta có đoạn mã giả như sau:
Hàm như sau:
- Với mọi tiến hành:
- Nếu thì:
- .
- .
- Nếu thì:
- Trả ra .
- Còn nếu thì:
- .
- Còn nếu thì:
- .
- Kết thúc điều kiện
- Nếu không:
- .
- Nếu thì:
- Trả ra .
- Kết thúc điều kiện
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Trả ra .
Kết thúc hàm
Về cơ bản, phương pháp này cũng có tính chất vét cạn: Ở đây thay vì thử hết các khả năng tô màu của cả chuỗi, thì gói gọn lại trong thử hết khả năng tô màu từng ô và kết luận.
Tính chất
Tính chất 1: Thuật toán chỉ tô các ô trống bên trong chuỗi , không đụng vào các ô có sẵn màu.
Chứng minh (Phản chứng):
Giả sử tồn tại ô nằm giữa và là ô được chỉnh sửa màu. Gọi là phiên bản cũ sao cho và .
Nếu hiển thị màu không tô được, thuật toán sẽ ngắt luôn vòng lặp và trả ra .
Nếu hiển thị màu tô được, thì trên lý thuyết sẽ không được tô và để trống, vì hai màu đều thỏa mãn điều kiện của chuỗi ô.
Những điều trên chứng tỏ giả thiết không tồn tại.
Tính chất 2: Thuật toán đảm bảo hoàn thiện các ô đen cố định bên trong chuỗi rỗng.
Chứng minh (Phản chứng):
Giả sử tồn tại ô nằm giữa và là ô đen cố định nhưng không được tô vào trong chuỗi rỗng. Vì là ô đen nên chắc chắn sẽ chỉ được tô khi .
Nếu không được tô, điều này có nghĩa là cũng hợp lệ.
Nếu hợp lệ, nghĩa là không phải là ô đen cố định. Điều này trái với giả thiết ban đầu.
Độ phức tạp
Độ phức tạp của :
Ở các trường hợp cơ sở, trong trường hợp tệ nhất, thuật toán vẫn phải thực hiện vòng lặp để kiểm tra các ô. Vậy .
Ở trường hợp đệ quy, ta thấy các trường hợp ô đã tô màu cũng giống với phương pháp 1, trừ việc có backtracking. Độ phức tạp của thuật toán vì thế vẫn là .
Trường hợp chưa được tô màu, độ phức tạp là: .
Vì có độ dài , nên độ phức tạp là .
Độ phức tạp của :
Vì hàm sử dụng vòng lặp qua các ô, số vòng lặp là .
Nhân số vòng lặp với số lần đệ quy, ta có: .
Độ phức tạp của phương pháp 2 là .
So sánh hai phương pháp trên
Hai phương pháp trên đều có độ phức tạp là , giải quyết được vấn đề tô chuỗi ô thông qua thỏa mãn 2 tính chất: Tô các ô trống trên cơ sở tôn trọng sự tồn tại các màu có sẵn, và đảm bảo tô các ô đen cố định khi chuỗi rỗng.
Tuy nhiên chúng ta sẽ đi sâu vào phân tích các "vấn đề con" ở hai phương pháp.
Ở phương pháp 1, thuật toán tô màu trực tiếp trên các ô đan xen với việc kiểm tra tính hợp lệ của màu sắc. Khi nút đệ quy thực hiện trên ô rỗng, thuật toán chia ra làm hai chuỗi phụ độc lập với nhau, sau đó truy ngược để tổng hợp các nút giống nhau giữa các chuỗi phụ.
Ở phương pháp 2, thuật toán giải quyết từng ô trong chuỗi thông qua phép lặp và thử. Ở mỗi ô , khi ô có màu, thuật toán chỉ thuần túy kiểm tra lại tính hợp lệ của toàn chuỗi, còn ở ô rỗng thì sẽ lại chia ra làm các chuỗi phụ để thử nghiệm. Đặc điểm của các chuỗi phụ này là có chung dữ liệu của chuỗi con .
Vì vậy, ta nhận định: Phương pháp 2 chia thành các bài toán con không độc lập với nhau về dữ liệu. Đây chính là tiền đề để ta áp dụng quy hoạch động vào giải chuỗi.
Phương pháp tối ưu
Áp dụng quy hoạch động
Chúng ta sẽ áp dụng phương pháp top-down memoization lên trên phương pháp 2.
Trên cơ sở hàm đệ quy , chúng ta sử dụng một bảng bộ nhớ hai chiều để lưu trữ bộ nhớ các kết quả mỗi khi trả ra kết quả sai. Gọi là bộ nhớ hai chiều, được khởi tạo với mọi .
Ở mỗi lần tiến hành , khi trả ra sai, sẽ ghi nhận .
Ở mỗi nút đệ quy của , điều kiện sẽ khống chế việc thuật toán đi xa hơn, bởi vậy độ phức tạp của các bài toán con xoay quanh ở các vòng lặp:
Độ phức tạp của phương pháp tối ưu là .
Một số cải tiến khác
Cải tiến để một số trường hợp hàm chạy ở độ phức tạp : Chúng ta sẽ thêm những ràng buộc ở các trường hợp và .
- Nếu |s|=0 thì:
- Với mọi tiến hành:
- .
- Kết thúc vòng lặp
- Trả ra .
- Còn nếu thì:
- .
- .
- Với mọi tiến hành:
- Nếu thì:
- .
- .
- .
- Nếu không:
- .
- .
- Kết thúc điều kiện
- Kết thúc vòng lặp
- Kết thúc điều kiện
Cải tiến để một số trường hợp hàm chạy ở độ phức tạp : Đối với chuỗi ô rỗng toàn phần, chúng ta tính toán số ô trắng tự do , sau đó lặp qua để tìm ra các chỉ mục của những ô tô đen cố định thông qua công thức toán nêu trên. Sau đó, chúng ta lặp qua danh sách chỉ mục trên để tô đen các ô, thay vì gọi các hàm đệ quy ban đầu.
Giải quyết toàn bộ khung
Áp dụng phương pháp giải chuỗi ô
Ta đến với mã giả sau đây:
Đầu vào: Ma trận rỗng , danh sách dãy số hàng , danh sách dãy số cột .
- Khởi tạo là tập hợp các chỉ mục hàng.
- Khởi tạo là tập hợp các chỉ mục cột.
- Khi tiến hành:
- Với mọi tiến hành:
- Tô màu hàng của ma trận dựa trên dãy số .
- Nếu hàm tô màu trả ra thì:
- Trả ra . // là ma trận rỗng
- Kết thúc điều kiện
- Thêm chỉ mục các ô trong hàng được tô mới vào tập hợp .
- Loại bỏ ra khỏi tập hợp .
- Kết thúc vòng lặp
- Với mọi tiến hành:
- Tô màu hàng của ma trận dựa trên dãy số .
- Nếu hàm tô màu trả ra thì:
- Trả ra .
- Kết thúc điều kiện
- Thêm chỉ mục các ô trong cột được tô mới vào tập hợp .
- Loại bỏ ra khỏi tập hợp .
- Kết thúc vòng lặp
- Kết thúc vòng lặp
- Nếu tồn tại một ô trong ma trận chưa được tô thì:
- Trả ra .
- Kết thúc điều kiện
- Trả ra .
Bước giải quyết khung trên đây áp dụng phương pháp bottom-up memoization: Các hàng và cột được giải lần lượt từ thấp lên cao, cho đến khi không còn hàng và cột nào có thể giải quyết thêm nữa.
Gọi là kích thước hàng × cột của ma trận .
Đặt
Vậy độ phức tạp của thuật toán, trên cơ sở áp dụng hàm tô màu tối ưu, là hoặc .
Áp dụng giải thuật tham lam
Khi chủ thể giải quyết bài toán là máy tính, các chỉ mục được thực hiện theo thứ tự từ nhỏ đến lớn. Tuy nhiên, người chơi thực tế không giải quyết bài toán như vậy. Họ có khuynh hướng chọn lựa hàng và cột "dễ giải quyết" để xử lý trước.
Vì vậy, chúng ta sẽ áp dụng giải thuật tham lam như sau, để phương pháp giải gắn với bản năng của con người hơn.
Một chuỗi ô ra sao được gọi là "dễ giải"? Qua toàn bộ những gì chúng ta phân tích từ đầu bài, có hai giá trị quyết định đến độ khó của chuỗi ô:
- Số ô trống trong chuỗi
- Giá trị : tương ứng với số ô trắng tự do trong chuỗi.
Bởi thế, ta đề xuất cách chấm điểm chuỗi ô như sau:
Ở mỗi vòng lặp trong bước giải khung, chúng ta sắp xếp thứ tự các chỉ mục trong và theo thứ tự tăng dần của giá trị , sau đó tiến hành tô màu các chuỗi ô theo thứ tự trên.
Câu hỏi: Giải thuật tham lam có tối ưu hóa việc giải quyết trò chơi không?
Đáp án: Trên lý thuyết, việc sắp xếp lại các chỉ mục theo thứ tự có độ phức tạp là . Hơn nữa, điều này thực hiện trước khi mỗi vòng lặp xảy ra, và không làm giảm đi số vòng lặp bắt buộc. Vì vậy thuật toán vẫn là và không tối ưu được phép giải trên máy tính.
Vét cạn

Chúng ta có mẫu puzzle như trong hình. Khi chúng ta áp dụng thuần túy phép giải khung như trên, thứ tự tiến hành sẽ là:
- Ở bước giải các hàng:
- Hàng 1 có cách xếp, có ô trắng tự do tương ứng với dãy số. Do nên không có ô đen cố định. Hàng 1 bị để trống.
- Hàng 2 có cách xếp, có ô trắng tự do tương ứng với dãy số. Do nên không có ô đen cố định. Hàng 2 bị để trống.
- trở thành tập rỗng.
- Ở bước giải các cột:
- Cột 1 có cách xếp, có ô trắng tự do tương ứng với dãy số. Do nên không có ô đen cố định. Cột 1 bị để trống và tương tự với 3 cột còn lại.
- trở thành tập rỗng. Do không có ô nào được tô nên vẫn là tập rỗng.
- Thuật toán kết thúc và trả ra với ma trận để trống toàn tập.
Để giải quyết vấn đề trên, chúng ta sẽ thay thế phần trả ra bằng việc trả ra dòng mã giả dưới đây:
- Gọi một ô trống bất kỳ trong ma trận dang dở.
- Tô màu bằng phương pháp giải khung ở trên khi . (Khởi tạo ).
- Tô màu bằng phương pháp giải khung ở trên khi . (Khởi tạo ).
- Ở mỗi hàm tô màu, đệ quy cho đến khi ma trận giải hết hoặc trả ra .
Chúng ta kết thúc với bức hình:

Độ phức tạp của thuật toán vét cạn là
Kết luận về cách giải ở người chơi
Từ việc phân tích thao tác của máy tính trong việc giải Nonogram puzzle thông qua các giải thuật khác nhau, ta đi đến kết luận về phương pháp giải của con người:
- Ở ma trận rỗng, người chơi chọn các hàng và cột có độ khó thấp nhất để giải trước.
- Người chơi lấp tối đa khung hình bằng các ô tô đen cố định và các ô trắng (nếu có).
- Khi ma trận được lấp một phần, xu hướng chọn hàng và cột thiên về hàng và cột có ít ô chưa tô màu nhất.
- Khi không thể giải bằng logic nữa, xu hướng vét cạn sẽ được áp dụng với các ô rỗng.
Tài liệu tham khảo
Về Nonogram: Đồ án học phần LU3IN003 - Algorithmique II niên khóa 2023-2024 của trường Đại học Sorbonne, Paris
Về các giải thuật:
- Tài liệu học học phần UL3IN003 - Algorithmique II của trường Đại học Sorbonne, Paris bởi các Phó Giáo sư Fanny P. và Olivier S.
- manhhomienbienthuy - Quy hoạch động - một thuật toán thần thánh
- Phong Tr. - Thuật toán tham lam - Greedy Algorithm
Về phần Toán học: Nỗ lực cá nhân có sự hỗ trợ của Tiến sĩ Manuel A., ChatGPT và Wikipedia
All rights reserved