代码地址
GitHub:https://github.com/JiuSiZhang/021700827
9.25更新,修改传参方式。
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | 1h | 0.5h |
Estimate | 估计这个任务需要多少时间 | 15h | 12.5h |
Development | 开发 | 1h | 1h |
Analysis | 需求分析 (包括学习新技术) | 3h | 2h |
Design Spec | 生成设计文档 | 1h | 0.5h |
Design Review | 设计复审 | 1h | 0.5h |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 1h | 0.5h |
Design | 具体设计 | 1h | 1h |
Coding | 具体编码 | 1h | 1h |
Code Review | 代码复审 | 1h | 1h |
Test | 测试(自我测试,修改代码,提交修改) | 1h | 1h |
Reporting | 报告 | 1h | 1h |
Test Repor | 测试报告 | 1h | 1h |
Size Measurement | 计算工作量 | 0.5h | 0.5h |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 0.5h | 1h |
合计 | 15h | 12.5h |
解题思路
这个题目其实很简单,本质就是搜索+回溯,我们只要写一个dfs函数和check函数就应该可以完成了,比较麻烦的可能是环境方面的设置和读入,话不多说,先上代码,然后一一解释。
dfs函数
基本的dfs函数,判断当前空有没用被填过,如有,则继续向下搜索,否则开始填数并进行验证记得回溯。
void dfs(int x)//dfs函数 递归填数
{
if (flag)//如果已经完成 直接返回
{
return;
}
if (x == m * m)//已经找完
{
output();
flag = 1;
return;
}
int r = x / m;//行
int c = x % m;//列
if (!s[r][c])//没填过就填这个空
{
for (int i = 1; i <= m; i++)
{
if (check(x, i))
{
s[r][c] = i;
dfs(x + 1);
s[r][c] = 0;//回溯
}
}
}
else//已经有数就跳过
{
dfs(x + 1);
}
}
check函数
这个函数的主要作用是check行和列是否合法,并判断是否为4 6 8 9,因为它们需要验证宫。
bool check(int x, int val)//第一个验证函数 验证行与列且 4 6 8 9时需要判断宫
{
int r = x / m;
int c = x % m;
for (int i = 0; i<m; i++)//行
{
if (s[r][i] == val)
{
return 0;
}
}
for (int i = 0; i<m; i++)//列
{
if (s[i][c] == val)
{
return 0;
}
}
if (m == 4)//4*4
{
if (check2(x, 2, 2, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 6)//6*6
{
if (check2(x, 2, 3, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 8)//8*8
{
if (check2(x, 4, 2, val))
{
return 1;
}
else
{
return 0;
}
}
else if (m == 9)//9*9
{
if (check2(x, 3, 3, val))
{
return 1;
}
else
{
return 0;
}
}
return 1;
}
check2函数
这个函数。用来判断小宫内是否合法。
bool check2(int x, int r, int c, int val)//第二个验证函数 判断宫
{
int a = x / m;
int b = x % m;
a = a / r * r;//行
b = b / c * c;//列
for (int i = a; i<a + r; i++)
{
for (int j = b; j<b + c; j++)
{
if (s[i][j] == val)
{
return 0;
}
}
}
return 1;
}
output函数
输出函数,将结果输出至文件,注意不一定忽略行末空格。
void output()//输出函数
{
for (int i = 0; i<m; i++)
{
for (int j = 0; j<m; j++)
{
fprintf(fp2, "%d", s[i][j]);//输出至文件
if (j < m - 1)
{
fprintf(fp2, " ");
}
}
fprintf(fp2, "\n");
}
}
main函数
注意读入方式,一开始不知道怎么通过命令行读入参数,后来看了先写的同学的博客的百度后才知道的。argv【2】,argv【4】,argv【6】,argv【8】分别对应m,n,输入文件名,输出文件名,然后我们定义两个FILE变量,通过fopen打开文件,再用fscanf读取数据。输出到文件用fprintf函数。
int main(int argc, char *argv[])
{
//std::ios::sync_with_stdio(false);
//cin.tie(0);
//m = atoi(argv[2]);//读取参数
//n = atoi(argv[4]);
//char *inputname = argv[6];
//char *outputname= argv[8];
string a, b, c, d;
a = "-m";
b = "-n";
c = "-i";
d = "-o";
fp1 = NULL;
fp2 = NULL;
for (int i = 1; i < argc; i++)
{
if (argv[i] == c)
{
fp1 = fopen(argv[++i], "r");//打开输入文件
if (fp1 == NULL)
{
return -1;
}
continue;
}
if (argv[i] == d)
{
fp2 = fopen(argv[++i], "w");//打开输出文件,清空文件
if (fp2 == NULL) //
{
return -1;
}
continue;
}
if (argv[i] == a)
{
m = argv[++i][0] - '0';
continue;
}
if (argv[i] == b)
{
n = argv[++i][0] - '0';
continue;
}
}
while (n--)
{
mst(s, 0);//初始化
flag = 0;
for (int i = 0; i<m; i++)//输入
{
for (int j = 0; j<m; j++)
{
fscanf(fp1, "%d", &s[i][j]);//文件读入
}
}
//fp2 = fopen(outputname, "a");//打开输出文件
dfs(0); //开始填数
if (n > 0)
{
fprintf(fp2, "\n");//输出至文件
}
}
if (fp1 != NULL)
{
fclose(fp1);//关闭输入文件
}
if (fp2 != NULL)
{
fclose(fp2);//关闭输出文件
}
return 0;
}
难点
其实本次的编程并不难,难的应该是一系列没有学过的操作,比如文件操作,vs工程操作,github操作,这些需要自己去网上找资料学习,不过一遍之后的确熟悉了很多。
Code Quality Analysis检查结果
性能分析工具Studio Profiling Tools分析结果
测试结果展示
3*3
4*4
5*5
6*6
7*7
8*8
9*9
全家福
收获与心路历程
学到很多东西,以前自己只会打打题目,其他东西都不怎么忙会用,通过这次实验,学到了很多东西,github,vs,文件操作。以及一些项目管理与测试的知识。