GitHub项目地址:
我的github项目链接:https://github.com/54zhazhahui/131700114
需求
实现一个命令行程序,不妨称之为Sudoku。
百度百科简介:
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
具体任务:
现在我们想一步一步来,完成从三宫格到九宫格的进阶;完成三宫格和其他博客任务,就算过了初级考核,其他的算升级。具体各阶规则如下:
三宫格:盘面是33。使1-3每个数字在每一行、每一列中都只出现一次,不考虑宫;
四宫格:盘面是22四个宫,每一宫又分为22四个小格。使1-4每个数字在每一行、每一列和每一宫中都只出现一次;
五宫格:盘面是55。使1-5每个数字在每一行、每一列中都只出现一次,不考虑宫;
六宫格:盘面是23六个宫,每一宫又分为32六个小格。使1-6每个数字在每一行、每一列和每一宫中都只出现一次;
七宫格:盘面是77。使1-7每个数字在每一行、每一列中都只出现一次,不考虑宫;
八宫格:盘面是42八个宫,每一宫又分为24八个小格。使1-8每个数字在每一行、每一列和每一宫中都只出现一次;
九宫格:盘面是33九个宫,每一宫又分为3*3九个小格。使1-9每个数字在每一行、每一列和每一宫中都只出现一次;
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10分钟 | 5分钟 |
Estimate | 估计这个任务需要多少时间 | 36小时 | 48小时 |
Development | 开发 | 4小时 | 6小时 |
Analysis | 需求分析 (包括学习新技术) | 2小时 | 6小时 |
Design Spec | 生成设计文档 | 2小时 | 4小时 |
Design Review | 设计复审 | 2小时 | 1小时 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 1小时 | 4小时 |
Design | 具体设计 | 1小时 | 1小时 |
Coding | 具体编码 | 2小时 | 1小时 |
Code Review | 代码复审 | 2小时 | 30分钟 |
Test | 测试(自我测试,修改代码,提交修改) | 2小时 | 8小时 |
Reporting | 报告 | 1小时 | 1小时 |
Test Repor | 测试报告 | 30分钟 | 10分钟 |
Size Measurement | 计算工作量 | 10分钟 | 20分钟 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30分钟 | 45分钟 |
合计 | 19.2小时 | 33.2小时 |
解题思路
看完题目要求之后,我就自己动手画了了一个九宫格。在做题目的过程中,我慢慢体会到解题的思路。基本上就是,排查一个空可能填的数,当只剩下一种可能的时候就可以填了。但是有时候可能有两种或者更多的可能值,这就需要我们去试探了。因为题目比较简单,思路也很快就出来了。用正常的搜索+递归,就能把题目要求的唯一解(标准)数独算出来。比较难的就需要我们去试探了。听说有道史上最难的数独,只有最聪明的人才能解出来,希望我的程序可以算出来。
源代码
先看一下主函数吧。这个文件输入输出我整整搞了两天,最后发现两个问题。一个是vs2017建立项目的时候,应该选择建立Windows控制台应用程序。还有一个问题就是input.txt和output.txt写成input.txt.txt和output.txt.txt。讲真的,我自闭了,心态被搞崩了。看了好多资料,用了无数的方法都不行。最后可以实现的那一刻,心里舒服了好多。
主函数
int main(int argc, char** argv)
{
int i, j;
ifstream ifp;
ofstream ofp;
m = atoi(argv[2]);//宫阶数
n = atoi(argv[4]);//盘面数
choosemn();
ifp.open(argv[6]);
if (!ifp.is_open())//判断文件是否成功打开
cout << "文件打开失败" << endl;
ofp.open(argv[8]);
if (!ofp.is_open())
cout << "文件打开失败" << endl;
while (n > 0)
{
resetmay();
for (i = 0; i < m; i++)//输入数独盘面
{
for (j = 0; j < m; j++)
{
ifp >> a[i][j].num;
}
}
inputsign();
scansign();
for (i = 0; i < m; i++)//输出解出的数独
{
for (j = 0; j < m; j++)
{
if (j < (m - 1))
ofp << a[i][j].num << " ";
else
ofp << a[i][j].num;
}
ofp << endl;
}
ofp << endl;
n--;
}
return 0;
}
结构体定义数据
struct number //每个数的结构体 sa
{
int sign;//定义一个标记,表示这个数还有几种可能的取值
int maybe[9] = { 1,2,3,4,5,6,7,8,9 };//定义num的可能取值
int num;//定义num的确定值
}a[9][9];
核心代码
核心代码是两个函数,一个唯一值函数,一个排查函数。
唯一值函数
void onlyone(int i, int j)//这个函数是把唯一解求出来; { for (int k = 0; k < line; k++) //唯一解在maybe里面 { if (a[i][j].maybe[k] != 0) { a[i][j].num = a[i][j].maybe[k]; a[i][j].maybe[k] = 0; a[i][j].sign = 0; del(i, j); break; } } }
排查函数
void deletehl(int i, int j)
{
for (int k = 0; k < line; k++)//把同一行的sign减一,把maybe的可能取值变零
{
if (a[i][k].maybe[a[i][j].num - 1] != 0)//如果不等0,说明还没去掉这个可能;
//等0的话,说明前面已经去掉了,sign不用再减一了;
{
a[i][k].maybe[a[i][j].num - 1] = 0;
a[i][k].sign--;
if (a[i][k].sign == 1)
{
onlyone(i, k);
}
}//把同一行的相同可能值删掉
if (a[k][j].maybe[a[i][j].num - 1] != 0)
{
a[k][j].maybe[a[i][j].num - 1] = 0;
a[k][j].sign--;
if (a[k][j].sign == 1)
{
onlyone(k, j);
}
}//把同一列的相同可能值删掉
}
}
void deletegong(int i, int j)
{
//宫的首地址计算 //x,y;这个宫的首个数的地址为a[x][y] ;
int x, y;
x = (i / gongrow) * gongrow;
y = (j / gongline) * gongline;
for (int gi = 0; gi < gongrow; gi++)
for (int gj = 0; gj < gongline; gj++)//把同一宫的sign减一,把maybe【num】的取值变零
{
if (a[gi + x][gj + y].maybe[a[i][j].num - 1] != 0)
{
a[gi + x][gj + y].maybe[a[i][j].num - 1] = 0;
a[gi + x][gj + y].sign--;
if (a[gi + x][gj + y].sign == 1)
{
onlyone(gi + x, gj + y);
}
}
}
}
唯一值函数就是当一个空的可能性只有一种的时候,就可以填入了。
排查函数就是把同一列或同一行或同一宫的,已存在的值的可能性排除掉。
数据测试
性能分析
总结
这次作业花了我很长的时间,但是也是收获满满。比如说学会了在github上传代码,还有vs2017的使用,还有文件输入输出!!!在写作业的过程中遇到了很多困难,很多bug,很多意想不到的问题,当是通过自己的努力,一步步攻克过来,最后成功解决。我觉得这个过程才是最重要的,也是我这次最大的收获。