GitHub项目地址:https://github.com/fishred2941214/2019SoftwareEngineer/tree/master/031702409/src
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 1小时 | 0.5小时 |
Estimate | 估计这个任务需要多少时间 | 33小时 | 36小时 |
Development | 开发 | 8小时 | 10小时 |
Analysis | 需求分析 (包括学习新技术) | 8小时 | 7小时 |
Design Spec | 生成设计文档 | 1小时 | 1.5小时 |
Design Review | 设计复审 | 0.5小时 | 0.5小时 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 1小时 | 0.5小时 |
Design | 具体设计 | 2小时 | 1小时 |
Coding | 具体编码 | 5小时 | 7小时 |
Code Review | 代码复审 | 2小时 | 1小时 |
Test | 测试(自我测试,修改代码,提交修改) | 1小时 | 2小时 |
Reporting | 报告 | 2小时 | 3小时 |
Test Repor | 测试报告 | 1小时 | 1小时 |
Size Measurement | 计算工作量 | 0.5小时 | 0.5小时 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 1小时 | 1小时 |
合计 | 34小时 | 36.5小时 |
解题思路和设计过程:
-
第一阶段
我首先学习的是从来没有接触过的命令行输入。刚开始看到命令行输入的时候,我是懵的,看完助教老师发的有关命令行输入知识的文章以后,又在网上找了很多关于这个的文章,结合理解了一下才明白。之后又去百度了怎么用txt文件输入输出,然后就尝试了fopen(),fwrite(),fscanf()一系列函数,fscanf的最后一个系数应该是地址,我刚开始把变量名直接输进去,一直不成功、、、中间又换了很多函数用,反正研究这两个真的用了好久好久、、、、后来写完的主函数大概是这样:
int main(int argc, char* argv[])
{
int i;
int j;
int z;
FILE* fp1;
FILE* fp2;
order = atoi(argv[2]);//命令行输入的索引0是程序,索引1是‘-m’,索引2就是需要的参数(数独的阶数)
num = atoi(argv[4]);
fp1 = fopen("input.txt", "r");//打开fp1,我原来没用只读模式,数据居然会没掉!
fp2 = fopen("output.txt", "w");//打开fp2,用只写模式
if (fp1 == NULL)
{
cout << "fp1打不开嗷";
exit(1);
}
if (fp2 == NULL)
{
cout << "fp2打不开嗷";
exit(1);
}
for (i = 0; i < num; i++)
{
for (j = 0; j < order; j++)
{
for (z = 0; z < order; z++)
{
fscanf(fp1, "%d", &soduku[j][z]);//把从fp1里读出来的数据输到数组里去
}
}
for (j = 0; j < order; j++)
{
for (z = 0; z < order; z++)
{
fprintf(fp2, "%d ", soduku[i][j]);//完成后的数独,写进fp2
}
fprintf(fp, "
");
}
fprintf(fp, "
");
}
fclose(fp2);
return 0;
}
-
第二阶段
然后就开始写数独代码了。我想到的办法就是把要填格子所在的行遍历一遍,列在遍历一遍,(4,6,8,9需要遍历宫),然后填入满足条件的数,如果接下来的格子中不存在满足条件的数,就返回上一个格子修改数字,如果还是不行就再返回再上一个格子......一直一直回溯,之前数据结构中也有类似这种的搜索方法。这种方法我认为三宫格和九宫格的实现并没有什么很大的区别。我写了两个函数。
第一个函数insert(),向宫格中尝试插入各种数,用递归的方式,当所有格子都填满时跳出:
void insert(int count, FILE* fp)
{
int row = count / order;
int col = count % order;
int i;
int j;
bool flag;
if (count == order * order)//一开始用的是order*order-1,会少填一个格子
{
cout << "结果:" << endl;
for (i = 0; i < order; i++)
{
for (j = 0; j < order; j++)
{
fprintf(fp, "%d ", soduku[i][j]);
}
fprintf(fp, "
");
}
fprintf(fp, "
");
return;
}
if (soduku[row][col] != 0)
{
insert(count + 1, fp);
}
else
{
for (i = 1; i <= order; i++)
{
soduku[row][col] = i;
flag = judge(count, soduku[row][col]);//测试这个数能不能满足条件的函数
if (flag)
{
insert(count + 1, fp);\去填下一个格子
}
}
soduku[row][col] = 0;
}
}
刚开始其实并没有把文件的输出也放在这个函数里,但是在主函数里调用这个函数时,数组的值没有改变,当下只能想到这个解决办法。
刚开始judge里面的第二个参数,填的一直是i,然后就导致了数组填到某一处找不到满足条件数字需要回溯时回溯失败,填到这里就进行不下去了。后来改了很多地方都不对,改了i就对了,但我现在还没想明白原因、、、、
第二个函数judge,判断填入的数是不是满足条件的:
bool judge(int count, int target)
{
int row = count / order;
int col = count % order;
int i;
int j;
for (i = 0; i < order; i++)
{
if (soduku[row][i] == target && i != col)
{
return false;
}
}//判断同一行
for (i = 0; i < order; i++)
{
if (soduku[i][col] == target && i != row)
{
return false;
}
}//判断同一列
int s = sqrt(order);
if (order == 9 || order == 4)
{
int realrow = row / s * s;
int realcol = col / s * s;//找出该格所属宫的起始行数和列数
for (i = realrow; i < realrow + s; i++)
{
for (j = realcol; j < realcol + s; j++)
{
if (soduku[i][j] == target && i != row && j != col)
{
return false;
}
}
}
}//,判断同一宫,9和4都是一个数的平方,数所在的宫格比较好找
if (order == 6)
{
int realrow = row / 2 * 2;
int realcol = col / 3 * 3;
for (i = realrow; i < realrow + 2; i++)
{
for (j = realcol; j < realcol + 3; j++)
{
if (soduku[i][j] == target && i != row && j != col)
{
return false;
}
}
}
}//6和8其实也差不多,要仔细看题目的宫格是怎么分的
if (order == 8)
{
int realrow = row / 4 * 4;
int realcol = col / 2 * 2;
for (i = realrow; i < realrow + 4; i++)
{
for (j = realcol; j < realcol + 2; j++)
{
if (soduku[i][j] == target && i != row && j != col)
{
return false;
}
}
}
}
return true;
}
还有一些全局变量,为了在函数中也能直接使用这些变量:
int soduku[9][9];
int order;
int num;
数据测试:
3到9宫格的测试图:
如果输入的数组是解不唯一的话,会输出多个解,因为递归返回时进入上一层,尝试填数的循环还是会继续循环下去:
Code Quality Analysis检测:
Studio Profiling Tools检测:(上面是有多解的六宫格,下面是九宫格)看不太懂这个测试是什么意思
心路历程:
刚打开作业的时候,我的心情还算比较轻松,因为对于数独我们并不陌生,至少这并不是一个让我完全看不懂的题目。但是当我真正开始做题目的时候,我才发现,数独的解法只是这个题目的一部分、、、命令行输入还有用txt文件来输入输出还有学会一些我从来没使用过的软件也是这个作业一个很大很大的部分。一开始我是打算用JAVA来编写代码的,但是由于我最近才刚刚开始学习JAVA,对于这门语言还不是很熟练也不太有信心,而且以前学数据结构的时候也学习过一些关于数独的算法,所以最后还是决定用c++。
学习命令行输入和txt输入输出用了我比较多的时间,几乎和后面写代码花的差不多了。然后因为写代码时测试忘记要截图,后面自己从头到尾又做了一次测试、、、、、深刻的教训,以后一定要记住。通过这次作业,我还了解到了vs2019的很多用法,刚准备提交作业的时候发现自己没有预编译头文件,问了好多同学,上网查找了一个下午,从“为什么我建的项目没有stdafx.h”到“vs2019建项目流程”,最后才知道原来空项目是没有这个文件的、、、、建项目时要选择Windows桌面向导、、、、这次作业中犯的好多错误,希望以后不要再犯了!!!!!