如何使用C ++遍历Sudoku子网格?

时间:2019-01-07 05:55:32

标签: c++

我正在尝试进行在线Sudoku c ++测试问题。

我需要确定9x9数独板是否有效。仅需根据以下规则验证填充的单元格(某些单元格带有“。”以表示未填写):

  1. 每行必须包含1-9数字,且不能重复。
  2. 每列必须包含1-9数字,且不能重复。
  3. 网格的9个3x3子框中的每个子框中必须包含数字1-9,且不能重复。

对于我的解决方案,我遍历每行,每一列和每个子框网格。将这些数字添加到地图。并检查该地图是否重复。

我很确定我已经解决了标准12,但是我在想像如何遍历子框3x3网格时遇到了麻烦。因此,我改编了some code found here,说实话,我仍然无法完全解决问题。我认为这可能是造成问题的原因。

我该如何解决标准3?

示例输入,正确答案应返回False,但我的代码返回True:

[
[".",".","4",".",".",".","6","3","."],
[".",".",".",".",".",".",".",".","."],
["5",".",".",".",".",".",".","9","."],
[".",".",".","5","6",".",".",".","."],
["4",".","3",".",".",".",".",".","1"],
[".",".",".","7",".",".",".",".","."],
[".",".",".","5",".",".",".",".","."],
[".",".",".",".",".",".",".",".","."],
[".",".",".",".",".",".",".",".","."]
]

我(破)的解决方案:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {

        //Iterate over each row
        for (int x = 0; x < 9; x++) {
            //Add row numbers to map
            map<char, int> row_nums {};
            for (int i = 0; i < 9; i++) {
                if (board[x][i] != '.') {
                    row_nums[board[x][i]]++;
                }
            }
            //Return false if duplicates found in row map
            for (auto it = row_nums.begin(); it != row_nums.end(); ++it) {
                if (it->second > 1) {
                    return false;
                }
            }
        }

        //Iterate over columns
        for (int i = 0; i < 9; i++) {
            //Add column numbers to map
            map<char, int> col_nums {};
            for (int y = 0; y < 9; y++) {
                if (board[i][y] != '.') {
                    col_nums[board[i][y]]++;
                }
            }
            //Return false if duplicates found in column map
            for (auto it = col_nums.begin(); it != col_nums.end(); it++) {
                if (it->second > 1) {
                    return false;
                }
            }
        }

        //Iterate over the 3x3 sub-boxes and add numbers to a map
        //I think this is where I am stuck
        for (int x = 0; x < 9; x++) {
            for (int y = 0; y < 9; y++) {
                map<char, int> box_nums {};
                for (int bx = (x/3)*3; bx < (x/3)*3 + 3; bx++) {
                    for (int by = (y/3)*3; by < (y/3)*3 + 3; by++) {
                        if (board[bx][by] != '.') {
                            box_nums[board[bx][by]]++;
                        }
                    }
                }
                //Return false if duplicates found in column map
                for (auto it = box_nums.begin(); it != box_nums.end(); it++) {
                    if (it->second > 1) {
                        return false;
                    }
                }
            }            
        }

        //Else return true
        return true;
    }
};

2 个答案:

答案 0 :(得分:2)

您共享的示例是有效的Sudoku w.r.t子框。第4列中有一个问题,其中有两个5。必须更改列检查中的逻辑,以遍历每一行以保持列固定。

 //Iterate over columns
        for (int i = 0; i < 9; i++) {
            //Add column numbers to map
            map<char, int> col_nums {};
            for (int y = 0; y < 9; y++) {
                if (board[y][i] != '.') {
                    col_nums[board[y][i]]++;
                }
            }
            //Return false if duplicates found in column map
            for (auto it = col_nums.begin(); it != col_nums.end(); it++) {
                if (it->second > 1) {
                    return false;
                }
            }
        }

这应该可以解决您的问题。

不确定子框的问题,但这是另一种无需进行(bx/3)*3等操作即可获取子框的方法

for (int x = 0; x < 9; x+=3) {
    for (int y = 0; y < 9; y+=3) {
        map<char, int> box_nums{};
        for (int bx = x; bx < x + 3; bx++) {
            for (int by = y; by < y + 3; by++) {
                if (board[bx][by] != '.') {
                    box_nums[board[bx][by]]++;
                }
            }
        }
        //Return false if duplicates found in column map
        for (auto it = box_nums.begin(); it != box_nums.end(); it++) {
            if (it->second > 1) {
                return false;
            }
        }
    }
}

答案 1 :(得分:2)

首先,您不需要std::map,并使用第二个循环来验证是否有大于1的计数,只需使用std::set,并且如果insert操作返回false则意味着找到了重复项。其次,您只能拥有3个std::set数组,并一次遍历所有行和列,然后为每个项目找到合适的std::set

const size_t size = 9;
using cset = std::set<char>;
using sets = std::array<cset,size>;

sets columns, rows, squares;

for( size_t i = 0; i < size; ++i ) {
    for( size_t j = 0; j < size; ++j ) {
        char n = board[i][j];
        if( not checkSet( columns[i], n ) ) return false;
        if( not checkSet( rows[j], n ) ) return false;
        if( not checkSet( squares[i/3 + j/3*3], n ) ) return false;
    }
}
return true;

其中checkSet()可以这样简单:

bool checkSet( cset &s, char n )
{
     return s.insert( n ).second;
}

注意:如果您关心效率,则应使用std::array<bool,size>而不是std::set,将字符转换为数字0-8并将其用作该数组中的索引。