软工个人项目——数独(三)
前段时间一直在准备考试,数独项目进展比较缓慢,现在继续记录进程。本篇博客主要记录数独解决部分的设计实现和两个模块的汇总及main.cpp的实现。
Github项目地址:个人数独项目
最终代码以github为准
1.Solve_Sudoku.cpp的实现
数独的解决方案有很多思路,我首先想到的是用回溯或者DFS进行解题。这里我先选用了回溯法实现,之后在性能优化阶段可以改用DFS进行对比。
回溯法实现比较常规,一个solve()函数用于处理输入的数独,一个Print()函数用于将解决的终局打印出来。
void Solve_Sudoku(int r, int c)
{
while (sudoku[r][c] != '0') //找到一个空的数独位置
{
if (c < 8)
c++;
else //再来一轮
{
c = 0;
r++;
}
if (r == 9) //找到了一个答案即9x9数独中没有0,那就是找到了一个解答
{
Found_ans = true;
return;
}
}
bool Can_Search = false; //标记回溯算法中当前结点是否可以搜索
for (int i = 1; i <= 9; i++)
{
if (Check_Vis(r, c, i))
{
Can_Search = true; //标记可以搜索
Set_Vis(r, c, i); //当前结点搜索过
sudoku[r][c] = i + '0';
Solve_Sudoku(r, c);
if (Found_ans) //剪枝
return;
Can_Search = false;
Reset_Vis(r, c, i);
sudoku[r][c] = '0';
}
}
}
Print()函数用于打印,同时也方便在main源文件中调用:
void Print()
{
int pos = 0;
if (Is_First_Task == false) //如果不是第一个问题,就在求解之前输出一行空行
OutputFileSolveSudoku << endl; //输入
else
Is_First_Task = false;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (j != 9)
{
Solve_data[pos++] = sudoku[i][j];
Solve_data[pos++] = ' ';//存入空格
}
else
Solve_data[pos++] = sudoku[i][j];
}
Solve_data[pos++] = '\n';//存入换行符
}
OutputFileSolveSudoku << Solve_data; //一个数独终局整体以字符串的形式输出
}
这里吸取了生成终局实现时的经验,直接开了一个大数组进行存储,方便读取写入,减少运行时间。
代码汇总 main.cpp的实现
完成了终局生成和数独求解两个分别的模块后,我开始对这两个模块进行汇总,通过main.cpp实现统一输入、分别调用。
main.cpp只包含main函数。首先通过
FILE* fp1 = fopen(argv[2], "r");
获取输入命令,然后对输入命令进行判断。如果是求解数独但指向文件为空,显示“非法输入!”,如果输入为生成终局,按72为一轮调用sudoku_generate.cpp依次生成;如果为求解数独,则使用fgets()将数独读入到buffer数组中,再调用solve函数依次处理。
int main(int argc, char* argv[])
{
FILE* fp1 = fopen(argv[2], "r");
if (strcmp(argv[1], "-s") == 0 && fp1 == NULL)
{
printf("非法输入!\n");
return 0;
}
if (argc == 3 && strcmp(argv[1], "-c") == 0)
{
int len = strlen(argv[2]); //将命令行字符串转换为整数
for (int i = 0; i < len; i++)
{
if (argv[2][i] >= '0' && argv[2][i] <= '9')
{
int value = argv[2][i] - '0';
for (int j = 1; j <= len - 1; j++)
value *= 10;
n += value;
}
else
{
printf("Please input a legal number!\n");
return 0;
}
}
if (n > 1000000 || n <= 0)
{
printf("Please input a number from 1 to 1000000!\n");
return 0;
}
int round; //需要生成数独终局的轮数,一轮生成72个
if (n <= 72)
round = 1;
else
round = n / 72 + 1;
while (round)
{
int demand; //每一轮需要生成的矩阵数
Initial(); //初始化数独的第一行
if (round != 1) //不是最后一轮
demand = 72;
else //是最后一轮
demand = n % 72;
Produce_Sudoku(demand);
--round;
}
}
else if (argc == 3 && strcmp(argv[1], "-s") == 0)
{
char buf[110];
int count = 0;
while (fgets(buf, 20, fp1))
{
if (strcmp(buf, "\n") == 0) //遇空行跳过
continue;
for (int i = 0; i <= 16; i++)
{
if (i % 2 == 0)
{
a[count][i / 2] = buf[i];
Set_Vis(count, i / 2, buf[i] - '0');
}
}
count++;
if (count == 9)
{
Found_Ans = false;
Solve_Sudoku(0, 0); //回溯解数独
Print();
memset(vis, 0, sizeof(vis));
count = 0;
}
}
}
else
printf("非法输入!\n");
return 0;
}