如何使用 C++ 迭代数独子网格?

Posted

技术标签:

【中文标题】如何使用 C++ 迭代数独子网格?【英文标题】:How to iterate over Sudoku sub-grid using c++? 【发布时间】:2019-01-07 05:55:31 【问题描述】:

我正在尝试做一个在线数独 c++ 测试题。

我需要确定 9x9 数独板是否有效。只有填充的单元格需要根据以下规则进行验证(有些单元格有'.'表示它们没有填写):

    每行必须包含数字 1-9,不得重复。 每列必须包含数字 1-9,不得重复。 网格的 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;
    
;

【问题讨论】:

你不是在问问题。请阅读How to Ask。另外,如果您对某些代码有问题,请先提取minimal reproducible example。 添加了一个明确的问题,以防它不够清楚。所有代码都是查看我的解决方案所必需的,答案基于我提供的内容。 【参考方案1】:

首先,您不需要std::map 并使用第二个循环来验证是否有任何计数大于1,只需使用std::set,如果插入操作返回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&lt;bool,size&gt; 而不是std::set,将您的字符转换为数字 0-8 并将其用作该数组中的索引。

【讨论】:

我想我一直在关注这个直到squares[i%3 + (j%3) * 3]。你介意解释一下这部分是如何工作的吗?我很难想象这在 2D 平面上是如何工作的。 @TinyTiger 其实是我弄错了,应该是i/3 + j/3*3你可以看这里ideone.com/zlzWhZ【参考方案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;
            
        
    

【讨论】:

谢谢,这对我帮助很大。但现在我看到 line 3: 4863 Segmentation fault (core dumped) $command 错误。我将我的代码粘贴到一个在线编译器,它工作正常:cpp.sh/3v6l。知道有什么问题吗?谷歌搜索错误消息,显然这意味着我指向的是分配之外的内存区域。我不知道这是怎么回事。 在链接中,条件应该是by &lt; y + 3; 它显示为by &lt; by + 3,它永远不会中断。更改它应该可以解决问题

以上是关于如何使用 C++ 迭代数独子网格?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 OpenCV 获取数独网格的单元格?

如何仅使用 C++ 中的迭代器正确迭代 3D 向量? [关闭]

c++ 和 OpenGl:如何从类中创建网格对象实例

如何在 C++ 中反向迭代地图?

如何迭代栅格网格中的环?

有啥方法可以为 CuPy 计算设置线程数、块数和网格数?如何?