Hướng dẫn giải của Trò chơi SUDOKU
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.
Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.
Bạn phải đăng nhập để mở lời giải này.
Đăng nhậpTác giả: , , ,
Hiểu bài toán
Nhiệm vụ là giải một câu đố Sudoku tiêu chuẩn 9x9. Đầu vào là một xâu 81 ký tự, đại diện cho bảng Sudoku (hàng đầu tiên đến hàng thứ 9), trong đó các ô trống được ký hiệu bằng dấu chấm '.'. Đảm bảo câu đố có duy nhất một lời giải. Đầu ra cần là một xâu 81 ký tự chứa lời giải.
Các cách tiếp cận
Cách Backtracking (Quay lui) cơ bản
#include <bits/stdc++.h>
using namespace std;
char board[9][9];
// Kiểm tra tính hợp lệ của việc đặt ký tự ch tại hàng r, cột c
bool check(int r, int c, char ch) {
// Kiểm tra hàng
for (int i = 0; i < 9; i++)
if (board[r][i] == ch) return false;
// Kiểm tra cột
for (int i = 0; i < 9; i++)
if (board[i][c] == ch) return false;
// Kiểm tra ô 3x3
int br = (r/3)*3, bc = (c/3)*3;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[br+i][bc+j] == ch) return false;
return true;
}
bool solve() {
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
if (board[r][c] == '.') {
for (char ch = '1'; ch <= '9'; ch++) {
if (check(r, c, ch)) {
board[r][c] = ch;
if (solve()) return true; // Nếu tìm thấy lời giải, trả về true
board[r][c] = '.'; // Quay lui (backtrack)
}
}
return false; // Không điền được số nào -> lời giải sai
}
}
}
return true; // Đã điền hết các ô
}
int main() {
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
cin >> board[i][j];
solve();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++)
cout << board[i][j];
}
cout << endl;
return 0;
}
- Time Complexity: Thường rất nhanh cho Sudoku 9x9 (dưới 1ms). Trong trường hợp xấu nhất là ~O(9^(N))~ (với N là số ô trống), nhưng các ràng buộc giảm không gian tìm kiếm rất nhiều.
- Space Complexity: O(1) (không kể đệ quy), hoặc O(N) độ sâu đệ quy (N <= 81).
Đây là cách tiếp cận trực tiếp nhất. Ta duyệt qua từng ô của bảng. Nếu ô đó trống, ta thử các số từ 1 đến 9. Với mỗi số, ta kiểm tra xem việc đặt số đó vào có vi phạm quy tắc Sudoku hay không (hàng, cột, ô 3x3). Nếu hợp lệ, ta đệ quy để giải các ô tiếp theo. Nếu đệ quy thất bại, ta hoàn tác (backtrack) và thử số tiếp theo. Nếu tất cả các số đều không phù hợp, ta trả về false. Cách này tìm được lời giải do đảm bảo thử hết các khả năng.
Cách Backtracking với tối ưu hóa
#include <bits/stdc++.h>
using namespace std;
char grid[81];
// Kiểm tra tính hợp lệ dùng mảng boolean
bool check(int i, char num) {
int r = i / 9;
int c = i % 9;
for (int j = 0; j < 9; ++j) {
// Kiểm tra hàng và cột
if (grid[r * 9 + j] == num) return false;
if (grid[j * 9 + c] == num) return false;
// Kiểm tra ô 3x3
int br = (r / 3) * 3 + j / 3;
int bc = (c / 3) * 3 + j % 3;
if (grid[br * 9 + bc] == num) return false;
}
return true;
}
void dfs(int i) {
if (i == 81) {
printf("%s\n", grid);
exit(0); // Tìm thấy lời giải, kết thúc chương trình
}
if (grid[i] != '.') dfs(i + 1); // Bỏ qua ô đã có số
else {
for (char num = '1'; num <= '9'; ++num) {
if (check(i, num)) {
grid[i] = num;
dfs(i + 1);
grid[i] = '.'; // Quay lui
}
}
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
for(int i=0; i<81; ++i) cin >> grid[i];
dfs(0);
return 0;
}
- Time Complexity: Tối ưu hơn Backtracking cơ bản do giảm chi phí truy cập mảng 2 chiều và tối ưu hóa vòng lặp.
- Space Complexity: O(1) với mảng 1 chiều.
Thay vì dùng mảng 2 chiều, ta dùng mảng 1 chiều độ dài 81. Hàm kiểm tra được viết lại để sử dụng tính chất quỹ đạo của phép chia và lấy dư để tính vị trí các ô trong cùng 3x3 mà không cần lồng vòng lặp 3x3 riêng. Việc dùng exit(0) khi tìm thấy lời giải duy nhất giúp kết thúc lập tức thay vì phải trả về các mức đệ quy.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | Thường rất nhanh cho Sudoku 9x9 (dưới 1ms). Trong trường hợp xấu nhất là ~O(9^(N))~ (với N là số ô trống), nhưng các ràng buộc giảm không gian tìm kiếm rất nhiều. | O(1) (không kể đệ quy), hoặc O(N) độ sâu đệ quy (N <= 81). | Backtracking (Quay lui) cơ bản |
| 2 | Tối ưu hơn Backtracking cơ bản do giảm chi phí truy cập mảng 2 chiều và tối ưu hóa vòng lặp. | O(1) với mảng 1 chiều. | Backtracking với tối ưu hóa |
Bài học kinh nghiệm
- Sudoku là bài toán tối ưu hóa không gian tìm kiếm. Ràng buộc mạnh giúp loại bỏ các nhánh cây rất sớm.
- Thứ tự thử các số (1-9) không quan trọng đối với lời giải duy nhất, nhưng có thể ảnh hưởng đến tốc độ tìm thấy lời giải.
Lỗi thường gặp
- Lỗi off-by-one trong vòng lặp kiểm tra.
- Không xử lý đúng ký tự '.' khi nhập/xuất.
Bình luận