Giải game Sudoku bằng thuật quay lui bị lỗi ở một số tests
Hi mọi người. Mình có đang giải 1 problem về game Sudoku sau: http://ntucoder.net/Submission/Problem/3261
Ý tưởng của mình sẽ là quay lui từng ô từ trái sang phải trên mỗi dòng từ trên xuống dưới, tại mỗi ô đang xét (có giá trị 0) mình sẽ dùng hàm isSafe()
để kiểm tra tính hợp lệ của giá trị đặt vào ô đó. Nếu thỏa mãn tiếp tục đệ quy chạy tiếp, không thì chuyển sang giá trị khác (1 -> 9), nếu ko giá trị nào thỏa mãn thì quay lui lại trường hợp trước.
Mình cài đặt thuật toán như sau: https://ideone.com/4AxU4F
#include <iostream>
#include <vector>
#define N 9
using namespace std;
bool isSafe(const vector<vector<int>> &board, int row, int col, int val)
{
// check row
for (int i = 0; i < N; ++i) {
if (board[row][i] == val)
return false;
}
// check column
for (int i = 0; i < N; ++i) {
if (board[i][col] == val)
return false;
}
// first 3x3 square
if (row <= 2 && col <= 2) {
for (int i = 0; i <= 2; ++i) {
for (int j = 0; j <= 2; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// second 3x3 square
if (row <= 2 && col >= 3 && col <= 5) {
for (int i = 0; i <= 2; ++i) {
for (int j = 3; j <= 5; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// third 3x3 square
if (row <= 2 && col >= 6 && col <= 8) {
for (int i = 0; i <= 2; ++i) {
for (int j = 6; j <= 8; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// fourth 3x3 square
if (row >= 3 && row <= 5 && col <= 2) {
for (int i = 3; i <= 5; ++i) {
for (int j = 0; j <= 2; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// fifth 3x3 square
if (row >= 3 && row <= 5 && col >= 3 && col <= 5) {
for (int i = 3; i <= 5; ++i) {
for (int j = 3; j <= 5; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// sixth 3x3 square
if (row >= 3 && row <= 5 && col >= 6 && col <= 8) {
for (int i = 3; i <= 5; ++i) {
for (int j = 6; j <= 8; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// seventh 3x3 square
if (row >= 6 && row <= 8 && col <= 2) {
for (int i = 6; i <= 8; ++i) {
for (int j = 0; j <= 2; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// eighth 3x3 square
if (row >= 6 && row <= 8 && col >= 3 && col <= 5) {
for (int i = 6; i <= 8; ++i) {
for (int j = 3; j <= 5; ++j) {
if (board[i][j] == val)
return false;
}
}
}
// ninth 3x3 square
if (row >= 6 && row <= 8 && col >= 6 && col <= 8) {
for (int i = 6; i <= 8; ++i) {
for (int j = 6; j <= 8; ++j) {
if (board[i][j] == val)
return false;
}
}
}
return true;
}
bool solveSudoku(vector<vector<int>> &board, int row = 0, int col = 0)
{
// if reaching the final square of sudoku board
if (row == N && col == N) {
return true;
}
// if reaching the final square of each row
if (row < N && col == N) {
if (solveSudoku(board, row + 1)) {
return true;
}
return false;
}
// if the square doesn't have a number
if (board[row][col] == 0)
{
for (int i = 1; i <= 9; ++i)
{
if (isSafe(board, row, col, i))
{
board[row][col] = i;
if (solveSudoku(board, row, col + 1))
return true;
board[row][col] = 0; // backtrack
}
}
return false;
}
else {
// if the final square on each row has a number
if (row < N - 1 && col == N - 1) {
if (solveSudoku(board, row + 1, 0))
return true;
}
// if the final square of sudoku board has a number
if (row == N - 1 && col == N - 1) {
if (solveSudoku(board, row + 1, col + 1))
return true;
}
// normal
else {
if (solveSudoku(board, row, col + 1))
return true;
}
return false;
}
}
int main()
{
vector<vector<int>> sudoku(N);
for (int i = 0; i < N; ++i) {
sudoku[i].resize(N);
for (int j = 0; j < N; ++j) {
cin >> sudoku[i][j];
}
}
if (solveSudoku(sudoku)) {
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < N; ++j)
{
cout << sudoku[i][j] << " ";
}
cout << endl;
}
}
else cout << -1;
return 0;
}
Vì code khá dài dòng nên mình chú thích cho mọi người đọc dễ hiểu hơn tí.
Khi submit thì mình chỉ đúng 6/10 tests, cụ thể như sau: https://ideone.com/IPIVMf
(mình bị runtime error ở test 2, 3, 7, 9)
Dò mãi vẫn không biết code chạy vô tận ở chỗ nào, mong mọi người giúp đỡ ạ !
Xin cảm ơn.
do chạy chậm thôi em. Em giải Sudoku bằng tay như thế nào? Viết cho máy chơi như thế ấy :V Đầu tiên là lấy loại bỏ những giá trị cho trước khỏi các ô trống, mỗi ô trống ban đầu có thể có 9 giá trị, từ từ bị gạch bớt, ô trống nào chỉ có thể có 1 giá trị thì điền vô. Rồi khi chỉ còn lại các ô còn lại 2 hoặc nhiều hơn 2 giá trị thì mới dfs hoặc bfs mấy ô đó, ưu tiên mấy ô còn 2 giá trị trước :V Chứ mới vô nhào vô mò liền + ko gạch bỏ giá trị mà mỗi lần check isSafe xài 27 lần if else e là chậm
http://norvig.com/sudoku.html cách giải của ông già nào đó đây :V
Em cũng chả rành sudoku lắm anh, chỉ biết là một giá trị chỉ tồn tại duy nhất trên 1 hàng, cột và khu vực 3x3 chứa nó. Em hiện tại cũng chưa tìm hiểu tới được DFS và BFS nữa!
Đoạn code trên khi submit là bị RE chứ không phải TLE như em copy nhầm trong link IDEone anh (edit: https://ideone.com/IPIVMf), vậy là có phải do chạy vô tận ko ạ?
em xài quay lui tức là DFS rồi đó
RE là cái gì? runtime error thì em bật debug lên mà mò coi sai ở đâu
à coi cái hàm
solveSudoku
của em có checkrow == N && col == N
,row < N && col == N
, ko có checkrow == N && col < N
, chắc thiếu cái này làm runtime error?edit: có thể ko phải, cái if else tùm lum nhìn hoa mắt quá =)
code C++ giải sudoku cũ của anh đây (giải thuật của ông Norvig)
cách xài đơn giản 4 dòng trong main, sau này gặp sudoku thì láy ra xài thoi khỏi mất công nửa tiếng 1 tiếng ngồi giải =)
OOP với cái thuật toán đó là em hoa mắt mất anh @@
em bật chế độ debug lên debug code của em đi :V nó gặp runtime error chỗ nào nó nhảy tới đúng dòng đó liền mà
CB của em bị ngáo chẳng debug được, đặt breakpoint vào xong start thì nó hiện ra màn hình console cho mình nhập, nhập xong tắt chứ ko next line hay step into được gì cả :((
Em nghi nó bị lỗi chỗ này nè anh:
Khi
row = N - 1
vàcol = N
thì do hàmsolveSudoku(board, row + 1)
nó sẽ nhảy thànhsolveSudoku(board, N, 0)
, mà trong hàm ko có điều kiện nào setrow == N && col == 0
nên bị RE ?chắc vậy, sửa lại mấy cái if else sao cho nó rõ ràng đi =)
trước khi truy cập
board[row][col]
em phải bảo đảm row và col nằm trong khoảng 0 tới N-1, ở trên em chưa lọc trường hợp row == N && col < N nên sẽ có trường hợp nó truy cập board[N] gây lỗi thôiEm nghĩ là trường hợp
row == N && col < N
sẽ ko xảy ra (trừ trường hợpsolveSudoku(board, N, 0)
như em nói trên, mình sẽ set riêng) do trong hàm em ko gọi đệ quysolveSudoku(board, row + 1, col)
!Có vẻ suy đoán ở trên của em đúng anh :v
Em sửa thằng
if()
đầu tiên trong hàmsolveSudoku
thành:thì code đã AC :v
test cases bèo quá đây mà :V
em thử cái input này xem =)
thấy bảo là impossible 1400 giây mới ra =)
Đúng thiệt chứ
Để em nghiên cứu lại mấy cái DFS BFS với thuật toán anh nói xem cần cải thiện chỗ nào !
Btw, code trên hình như thuần quay lui chứ chưa có nhánh cận đúng ko anh? (vì em thấy problem này đặt trong category quay lui, nhánh cận) ?
OK, nó đứng luôn rồi anh !
nhánh cận là cái gì anh có biết đâu, hồi đó học hết giờ do ông thầy ổng ghiền phân tích big-O quá bỏ 2 3 tuần solo cái big-O nên tuần 12+ trở đi ổng lướt lướt phần đồ thị và quy hoạch động nên có nhớ gì đâu =) Sau này đồ thị chỉ biết mỗi DFS BFS còn QHĐ thì ú ớ toàn tập
Thuật toán này có vẻ đpt lũy thừa nên siêu chậm rồi!
Btw, đoạn input trên sau tầm 20 phút chạy thì KQ là ko có solution !!