0. Github项目地址
1. 解题思路
1.0 开发环境配置与学习
- 去年装好的vs,新增插件Unit Test Generator,学习简单的单元测试方法。
- 参考博客vs2015单元测试配置
1.1 算法思考
-
考虑深搜,最暴力的情况,每个格子可填的数为1-9,81个格子填数的方案数为 81^9,而数独有每行出现1-9,每列出现1-9,九宫格出现1-9的限制条件。
-
最开始时想根据行列块的限制条件进行深搜,顺序编号81个格子,一个格子一个格子往下填,但这种简单写法回溯时可能有要退特别多步的问题。 考虑深搜到某个格子无数可填时的回溯步骤,按行来填不存在行的限制,似乎可以用该行还未出现的数字与该行之前出现的但可替换的数字交换。这个算法夭折了,我还没思考清楚替换的策略,而且复杂度似乎不够优秀,而受到提示的我有了新的想法。
2. 设计实现
2.0 类、函数的设计
初步分为输入、生成数独、输出三个类。
-
Scan
- 处理控制台读入数据,check读入的几个参数,目前仅是简单的几种情况的check。
- 正确输入格式 sudoku.exe -c 20。
-
sudo
- init预处理。
- dfs深搜搜解。
- solve接收合法数独棋盘题目个数N,调用dfs函数求解。 -
Print
- 输出解。
2.1 流程图
3. 代码说明
3.0 思路解释
- 新算法生成辣。抛弃按格子填数的方案,新算法按照数来填,填完所有的1然后2...参照八皇后问题的思想,加上块的限制可以很好实现。
- 预处理。处理出每个格子所在的块,方便后面限制条件的实现。初始化用到的数组。
- dfs。void dfs(int num, int row),表示当前处理到num这个数字,当前要把num填在第row行。每个数字每行只能填一次,不存在行的限制,那么此时枚举所有的列,考虑列的限制以及格子所在块对于num的限制。
- 作业要求我的第一个格子为7,我不在dfs中特判实现而是输出时实现,将第一个格子所填的数与7交换即可。如我搜到的数独的第一个格子为6,将数独的所有1与所有6交换即可。
3.1 关键代码
void dfs(int num, int row) {
if (num == 9 && row >= 1) {//9这个数字可以不填,所以填到9时可以输出
!print
++ans_cnt;
if (ans_cnt >= need) exit(0);
return;
}
for (int j = 1;j <= 9;++j) {//枚举列
int pos = (row - 1) * 9 + j;//当前格子编号,编号1-81
//vis[pos] pos这个格子是否填数
//col_vis[num][j] num这个数字第j列是否填过
//blo_vis[num][j] num这个数字在pos所在块是否填过
if (vis[pos] || col_vis[num][j] || blo_vis[num][blo[pos]]) continue;
//无限制时将数填下,修改限制状态
a[pos] = num, vis[pos] = 1, col_vis[num][j] = 1, blo_vis[num][blo[pos]] = 1;
if (row == 9) dfs(num + 1, 1);//num这个数字第9行已经填了,考虑从第一行开始填下一个数字
else dfs(num, row + 1);
//回溯到num未填在这格的状态
a[pos] = 0, vis[pos] = 0, col_vis[num][j] = 0, blo_vis[num][blo[pos]] = 0;
}
}
4. 测试运行
4.0 小数据截图 n=3
4.1 n=1000
4.2 n=1000000
5. 性能改进与性能分析
- 算法上的小改进,深搜时全部填入1-8数字后即生成解。
- 输出的小改进,用putchar实现。
- 生成解文件路径改进,txt文件与exe文件在同个目录下。
- upd:参考其他博客提到的输出改进,压成字符串后puts输出,print耗时大大减少,本地测试总耗时由10s减少到2、3s
改进后:
改进前:
- 耗时大部分在dfs函数和print输出函数上
- dfs函数部分主要耗时在自身递归调用上
6. PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 60 |
Development | 开发 | 960 | 1100 |
· Analysis | · 需求分析 (包括学习新技术) | 510 | 580 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 120 | 130 |
· Coding | · 具体编码 | 120 | 120 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | 150 | 120 |
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 30 |
合计 | 1170 | 1280 |