【原创】一个基于简单剪枝的DFS解数独程序
问题来源:leetCode Sudoku Solver
Write a program to solve aSudoku puzzle by filling the empty cells.
Empty cells are indicated by the character *.*.
You may assume that there will be only one unique solution.
问题链接:
https://oj.leetcode.com/problems/sudoku-solver/
功能:
自动解数独题
大概方案:
采用以行为单位的DFS来寻找答案。
提纲:
0、对全部数据进行候选数预处理,记录下每个空格的候选数。
1、根据候选数表和当前结果表,以行为单位,递归产生所有可能的行,并加入结果数组,然后对每一个产生的可能行进行新一轮的递归调用。这里有一个互相递归调用的行为。
2、若无法产生可能的行或产生完所有行的下一级返回假,则返回假。
3、若有一个下级递归返回真或者最后一行中成功产生了可能的行,返回真。
主要方法概览:
(注意:候选数表为全局数组,所有方法可以直接访问)
0、bool tryRow(introw,vector<vector<int> > &res);
该方法为对某行进行数字试填,row为某行的行号,res为当前结果数组。
1、bool fillRow(introw,int col,vector<int> &now,vector<vector<int> >&res);
该方法为对特定的格子进行数字试填,row为行号,col为列号,now为当前行中已填的数字,res为结果数组。
2、以上两种方法互相递归调用。对于特定新行,调用tryRow方法,而填新行内的数则递归调用fillRow,当当前行已经填满后,在fillRow中调用新一轮的tryRow方法进入下一行。
Solution类的全部代码和注释:(\t被吞,用/*...*/来缩进)
#include <iostream>
#include <vector>
#include <cstdio>
using namespace std;
class Solution
{
public:
/*......*/ int sel[9][9][10];
// 候选数数组,sel[x][y][0]标记横坐标为x,纵坐标为y的格子是否有数字,有则为该数字,
// 待填则sel[x][y][num](1<= num <=9)为1表示该格子可选数包括num.
/*......*/ Solution()
/*......*/ {
/*.........*/ for (int i = 0;i < 9;i++)
/*............*/ for (int j = 0;j <9;j++)
/*...............*/ for (int p = 0;p <10;p++)
/*..................*/ sel[i][j][p] = 1;
/*......*/ }
/*......*/ voidsolveSudoku(vector<vector<char> > &board) // 主处理方法
/*......*/ {
/*.........*/ vector<vector<int>> res;
/*.........*/ init(board); // 进行候选数初始化处理
/*.........*/ tryRow(0,res);
/*.........*/ int len1 = board.size();
/*.........*/ int len2 = board[0].size();
/*.........*/ for (int i = 0;i <len1;i++)
/*............*/ for (int j = 0;j <len2;j++)
/*...............*/ if (board[i][j] == *.*)
/*..................*/ board[i][j] =res[i][j] + *0*;
/*......*/ }
/*......*/ bool tryRow(introw,vector<vector<int> > &res)
/*......*/ {
/*.........*/ if (row >= 9)
/*.........*/ {
/*............*/ return true;
/*.........*/ }
/*.........*/ bool flag = false;
/*.........*/ vector<int> now(10);
/*.........*/ flag =fillRow(row,0,now,res);
/*.........*/ return flag;
/*......*/ }
/*......*/ bool fillRow(int row,intcol,vector<int> &now,vector<vector<int> > &res)
/*......*/ {
/*.........*/ bool flag = false;
/*.........*/ if (col >= 9)
/*.........*/ {
/*............*/ res.push_back(now);
/*............*/ flag = tryRow(row +1,res);
/*............*/ if (flag == false)
/*...............*/ res.pop_back();
/*............*/ return flag;
/*.........*/ }
/*.........*/ if (sel[row][col][0] != 0)
/*.........*/ {
/*...............*/ now[col] =sel[row][col][0];
/*...............*/ flag = fillRow(row,col+ 1,now,res);
/*...............*/ return flag;
/*.........*/ }
/*.........*/ int hash[10] = {0}; // now中现存的数为1,该数组中为1的数不能选
/*.........*/ for (int i = col - 1;i >=0;i--) // 判断当前行中需要更新的候选数
/*............*/ hash[now[i]] = 1;
/*.........*/ update(row,col,hash,res); // 该函数继续更新该格子的候选数
/*......*/ for (int i = 1;i <= 9;i++)
/*.........*/ {
/*............*/ if (hash[i] == 0&& sel[row][col][i] == 1)
/*............*/ {
/*...............*/ now[col] = i;
/*...............*/ flag = fillRow(row,col+ 1,now,res);
/*...............*/ if (flag)
/*..................*/ return true;
/*............*/ }
/*.........*/ }/*...*/
/*.........*/ return flag;
/*......*/ }/*...*/
/*......*/
/*......*/ void update(int row,int col,int*hash,vector<vector<int> > &res)
/*......*/ {
/*.........*/ int top = getTop(row);
/*.........*/ int left = getLeft(col);
/*.........*/ for (int i = row - 1;i >=0;i--) // 既有结果列中的数不能选
/*.........*/ {
/*...............*/ hash[res[i][col]] = 1;
/*.........*/ }
/*.........*/ if (row > top)
/*.........*/ {
/*............*/ int count = row - top;
/*............*/ for (int i = 0;i <count;i++)
/*...............*/ for (int j = 0;j <3;j++)
/*..................*/hash[res[top+i][left+j]] = 1;
/*.........*/ }
/*......*/ }
/*......*/ voidinit(vector<vector<char> > &board)
/*......*/ {
/*.........*/ int len1 = board.size();
/*.........*/ int len2 = board[0].size();
/*.........*/ for (int i = 0;i <len1;i++)
/*............*/ for (int j = 0;j <len2;j++)
/*............*/ {
/*...............*/ if (board[i][j] != *.*)
/*...............*/ {
/*..................*/ setRow(i,board[i][j]- *0*);
/*..................*/ setCol(j,board[i][j]- *0*);
/*..................*/setCel(i,j,board[i][j] - *0*);
/*..................*/ sel[i][j][0] =board[i][j] - *0*;/*...*/
/*...............*/ }
/*...............*/ else
/*..................*/ sel[i][j][0] = 0;
/*............*/ }
/*......*/ }
/*......*/ void setRow(int i,int num)
/*......*/ {
/*.........*/ for (int j = 0;j < 9;j++)
/*.........*/ {
/*............*/ sel[i][j][num] = 0;
/*.........*/ }
/*......*/ }
/*......*/ void setCol(int j,int num)
/*......*/ {
/*.........*/ for (int i = 0;i < 9;i++)
/*.........*/ {
/*............*/ sel[i][j][num] = 0;
/*.........*/ }
/*......*/ }
/*......*/ void setCel(int x,int y,intnum)// 求方格的左上坐标
/*......*/ {
/*.........*/ int top,left;
/*.........*/ top = getTop(x);
/*.........*/ left = getLeft(y);
/*.........*/ for (int i = 0;i < 3;i++)
/*............*/ for (int j = 0;j <3;j++)
/*...............*/ sel[i+top][j+left][num]= 0;
/*......*/
// 该函数同getTop一样,用来求位于x行y列的格子所在的正方形格的位置。
/*......*/ int getLeft(int col)
/*......*/ {
/*.........*/ int tmp;
/*.........*/ int left;
/*.........*/ tmp = col % 9;
/*.........*/ if (tmp >= 6)
/*.........*/ {
/*............*/ left = 6;
/*.........*/ }
/*.........*/ else if (tmp >= 3)
/*............*/ left = 3;
/*.........*/ else
/*............*/ left = 0;
/*.........*/ return left;
/*......*/ }
/*......*/ int getTop(int row)
/*......*/ {
/*.........*/ int tmp;
/*.........*/ int top;
/*.........*/ tmp = row % 9;
/*.........*/ if (tmp >= 6)
/*.........*/ {
/*............*/ top = 6;
/*.........*/ }
/*.........*/ else if (tmp >= 3)
/*.........*/ {
/*............*/ top = 3;
/*.........*/ }
/*.........*/ else
/*............*/ top = 0;
/*.........*/ return top;
/*......*/ }
};
// 测试函数
int main(void)
{
/*...*/ vector<char> now;
/*...*/ vector<vector<char> >res;
/*...*/ int c;
/*...*/ Solution *p = new Solution();
/*...*/ while ((c = getchar()) != EOF)
/*...*/ {
/*......*/ if (c != '\n')
/*.........*/ now.push_back(c);
/*......*/ else
/*......*/ {
/*.........*/ res.push_back(now);
/*.........*/ now.clear();
/*......*/ }
}
/*...*/ p->solveSudoku(res);
/*...*/ return 0;
}
【原创】一个基于简单剪枝的DFS解数独程序的更多相关文章
- 用qpython3写一个最简单的发送短信的程序
到目前为止并没有多少手机应用是用python开发的,不过qpython可以作为一个不错的玩具推荐给大家来玩. 写一个最简单的发送短信的程序,代码如下: #-*-coding:utf8;-*- #qpy ...
- HDU 1426 Sudoku Killer(dfs 解数独)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1426 Sudoku Killer Time Limit: 2000/1000 MS (Java/Oth ...
- [原创]python之简单计算器(超详解,只有基本功能+-*/,还有括号处理)
不想看过程的话,直接看文章最后的正式源码 作业需求及分析: 流程图 https://www.processon.com/diagraming/580c5276e4b03c844a5a9716 初期感 ...
- 一个基于Socket的http请求监听程序实现
首先来看以下我们的需求: 用java编写一个监听程序,监听指定的端口,通过浏览器如http://localhost:7777来访问时,可以把请求到的内容记录下来,记录可以存文件,sqlit,mysql ...
- 用scala实现一个基于TCP Socket的快速文件传输程序
这是用scala实现的一个简单的文件传输程序. 服务端 package jpush import java.io.{DataInputStream, File, FileOutputStream} i ...
- 如何使用 Docker 部署一个基于 Play Framework 的 Scala Web 应用?
本文作者 Jacek Laskowski 拥有近20年的应用程序开发经验,现 CodiLime 的软件开发团队 Leader,曾从 IBM 取得多种资格认证.在这篇博文中,Jacek 分享了 Wars ...
- 新项目架构从零开始(三)------基于简单ESB的服务架构
这几个月一直在修改架构,所以迟迟没有更新博客. 新的架构是一个基于简单esb的服务架构,主要构成是esb服务注册,wcf服务,MVC项目构成. 首先,我门来看一看解决方案, 1.Common 在Com ...
- 使用python解数独
偶然发现linux系统附带的一个数独游戏,打开玩了几把.无奈是个数独菜鸟,以前没玩过,根本就走不出几步就一团浆糊了. 于是就打算借助计算机的强大运算力来暴力解数独,还是很有乐趣的. 下面就记录一下我写 ...
- Kcptun 是一个非常简单和快速的,基于KCP 协议的UDP 隧道,它可以将TCP 流转换为KCP+UDP 流
本博客曾经发布了通过 Finalspeed 加速 Shadowsocks 的教程,大家普遍反映能达到一个非常不错的速度.Finalspeed 虽好,就是内存占用稍高,不适合服务器内存本来就小的用户:而 ...
随机推荐
- java定义和实现接口
1.定义接口 使用interface来定义一个接口.接口定义同类的定义类似,也是分为接口的声明和接口体.当中接口体由常量定义和方法定义两部分组成.定义接口的基本格式例如以下: [修饰符] in ...
- JS的类型比较与转换图
完整比较图:红色:===橙色:==黄色:<= 和 >= 同时成立,== 不成立蓝色:只有 >=绿色:只有 <= https://www.zhihu.com/question/3 ...
- leetcode回文子串拆分-最小拆分次数
转载请注明来自souldak,微博:@evagle 上一篇是要输出所有的可能拆分,这回是要输出拆分次数最少的切割次数. 如果直接按照上一篇那么做的话,就会超时,因为我们在判断s[i][j]是否是回文的 ...
- CacheHelper工具类的使用
package com.bbcmart.util; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import ne ...
- ByteBuffer和String的互相转换
import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java ...
- java泛型 之 入门(interface)
一:泛型简单介绍: (1)所谓泛型,就是变量类型的參数化. 泛型是JDK1.5中一个最重要的特征.通过引入泛型,我们将获得编译时类型的安全和执行时更小的抛出ClassCastException的可能. ...
- MVC之文件上传1
MVC之文件上传 前言 这一节我们来讲讲在MVC中如何进行文件的上传,我们逐步深入,一起来看看. Upload File(一) 我们在默认创建的项目中的Home控制器下添加如下: public Act ...
- 基于FP-Tree的关联规则FP-Growth推荐算法Java实现
基于FP-Tree的关联规则FP-Growth推荐算法Java实现 package edu.test.ch8; import java.util.ArrayList; import java.util ...
- hdu3001(状压dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3001 题意:n 个城市已经 m 条路 以及对应路费 c,要求遍历所有城市最少的路费,每个城市不能超过2 ...
- 2014年辛星解读Javascript之用DOM动态操纵HTML元�
关于DOM,我们了解了能够用DOM操纵HTML的一些属性和样式,还能够为HTML元素绑定事件等等,那么接下来,我们将涉及到用DOM来动态的创建.删除HTML等一些操作,我的核心思路还是重实战,因此,代 ...