蹒跚的第一步
GitHub
https://github.com/JoyJia2/HOMEWORK
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | 1 | 1 |
Estimate | 估计这个任务需要多少时间 | 26 | 35 |
Development | 开发 | 2 | 2 |
Analysis | 需求分析 (包括学习新技术) | 2 | 2 |
Design Spec | 生成设计文档 | 1 | 3 |
Design Review | 设计复审 | 2 | 1 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 1 | 1 |
Design | 具体设计 | 2 | 1 |
Coding | 具体编码 | 5 | 12 |
Code Review | 代码复审 | 1 | 1 |
Test | 测试(自我测试,修改代码,提交修改) | 2 | 3 |
Reporting | 报告 | 2 | 2 |
Test Repor | 测试报告 | 2 | 3 |
Size Measurement | 计算工作量 | 2 | 2 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 1 | 1 |
合计 |
Sudoku1:完全遍历,暴力解答
就程序而言,我简单理解为三个部分:
文件(input.txt)读入=>处理=>写入文件(output.txt)
查阅了一下文献,写入和写出使用了简单的fostream和instream,这个还是比较好解决的。重头戏是处理,这里我犯了一个很严重的错误,这个错误也花费了我很多很多时间——我使用的最基本的思路。
阶段一:错误的出发点
我最初的思路将整个棋盘的每个子理解为一个结构体node,
struct node{
int pb[9]={1,2,3,4,5,6,7,8,9};
int part;
int num;
};
其中pb数组表示该位置可能的取值,可以直接默认为1-9,(在各阶宫中阶数会限制访问,这样n宫格的每个位置的可能性都是1-n),part用于判断四,六,八,九宫格的各个宫内进行判定,num则用来保存已经确定下的值。这样一来,就可以用最基本的排除法思路来做了。
需要一个 int solo(int *a)函数用于判断数组中是否只存在唯一的一个值,如果是则返回它,否则返回0。
int solo(int *a)
{
int temp=0,save=0;
for(int i=0;i<n;i++)if(a[i]!=0)
{
temp++;
save=a[i];
}
if(temp==1)return save;
else return 0;
}
需要一个 void kill(int a,int *b)函数用于已知数值缩小同行或者同列其他位置的取值可能性。(将排除掉的值设为0)
void kill(int a,int *b)
{
for(int i=0;i<n;i++)if(b[i]==a)b[i]=0;}
紧接着就是主要的handle函数,其用途是遍历结构体数组(即所有位置),逐个得到排除后的结果,并且将第一次产生的结果用于第二次遍历,如此循环直到某一次没有发生如何改动,跳出循环。
截至目前,函数已经可以处理三,五阶的宫格,但是再提高阶数就不能完成,即便接下来的部分有使用逐个假设的方法(代码不再列出)。
Sudoku2:递归回溯,清晰优雅
穷则思变,在下一步前查阅了资料,发现这种问题最好是用递归回溯方法,对比之下,顿时感觉到了算法的无穷魅力,于是基本上完全改变了我之前的代码。我两天的思考无疾而终,而这无疾而终的思考又让我产生了新的思考----闭门造车要不得,计划规划要完备。
#include<fstream>
using namespace std;
int Sudoku[9][9];
bool compare(int num,int ch_nowline,int ch_nowcolumn,
int ch_blockline,int ch_blockcolumn)
{
for(int i=0;i<=m-1;++i)
{
if(Sudoku[ch_nowline][i]==num||Sudoku[i][ch_nowcolumn]==num)
return false;
}
if(m==9)for(int i=0;i<=2;++i)//宫内检测
{
for(int j=0;j<=2;++j)
{
if(Sudoku[ch_blockline+i][ch_blockcolumn+j]==num)
return false;
}
}
if(m==8)for(int i=0;i<=3;++i)
{
for(int j=0;j<=1;j++)
{
if(Sudoku[ch_blockline+i][ch_blockcolumn+j]==num)
return false;
}
}
if(m==6)for(int i=0;i<=1;++i)
{
for(int j=0;j<=2;j++)
{
if(Sudoku[ch_blockline+i][ch_blockcolumn+j])
return false;
}
}
if(m==4)for(int i=0;i<=1;++i)
{
for(int j=0;j<=1;j++)
{
if(Sudoku[ch_blockline+i][ch_blockcolumn+j])
return false;
}
}
return true;
}
接下来是核心函数
bool work(int now_line,int now_column)//主要的处理函数
{
if(now_line==m)
{
return true;//如果将数独解完,返回true
}
else
{
int next_line,next_column,block_line,block_column;
next_column=now_column+1;
next_line=(next_column>=m?now_line+1:now_line);
next_column=(next_column>=m?0:next_column);
if(Sudoku[now_line][now_column]!=0)//如果当前坐标有数字,则对下一个坐标进行工作
{
if(work(next_line,next_column)) return true;//如果数独最终有解,则不断向前返回true
}
else
{
if(m==9)
{
block_line=(now_line/3)*3;//计算所在的3*3方格左上角坐标
block_column=(now_column/3)*3;
}
if(m==8)
{
block_line=(now_line/4)*4;//计算所在的4*2方格左上角坐标
block_column=(now_column/2)*2;
}
if(m==6)
{
block_line=(now_line/2)*2;//计算所在的2*3方格左上角坐标
block_column=(now_column/3)*3;
}
if(m==4)
{
block_line=(now_line/2)*2;//计算所在的2*2方格左上角坐标
block_column=(now_column/2)*2;
}
for(int i=1;i<=m;++i)
{
if(compare(i,now_line,now_column,block_line,block_column))
{
Sudoku[now_line][now_column]=i;
if(work(next_line,next_column)) return true;
}
}
Sudoku[now_line][now_column]=0;//回溯操作
return false;
}
}
}
void Input(int t,int *save)//将待测数据拷贝进特定数组
{
for(int i=0;i<=m-1;++i)
{
for(int j=0;j<=m-1;++j)
{
Sudoku[i][j]=save[t*m*m+i*m+j];
}
}
}
void Output(int t,int *save)//将结果拷贝到输出特定数组
{
for(int i=0;i<=m-1;++i)
{
for(int j=0;j<=m-1;++j)
{
save[t*m*m+i*m+j]=Sudoku[i][j];
}
}
}
现在开始实现主函数,主函数包括:
1.实现命令行参数读入。
2.实现文件导出数据(多说一句这里直接设置了一个长度500的字符数组来接收,本来可以完美运用内存,奈何vs2019不支持数组内为变量,这个故事告诉我们平时尽量使用正规编译器,dev c++还是有一定不足的)
3.每个棋盘依此处理,如何存进数组。
4.实现数据写入文件。
int main(int argc,char *argv[])
{
ifstream infile;
ofstream outfile;
m=argv[2][0]-48;
n=argv[4][0]-48;//获取宫格数和待测棋盘数
int save[500],cer=0;
infile.open(argv[6]);
char ch;
while(!infile.eof())
{
infile.get(ch);
if('0'<=ch&&ch<='9')save[cer++]=ch-48;
}
infile.close();
for(int t=0;t<n;t++)
{
Input(t,save);
if(work(0,0));
// else cout<<"wrong!"<<endl;
Output(t,save);
}
outfile.open(argv[8]);//开始写入
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
for(int k=0;k<m;k++)
{
outfile<<save[i*m*m+j*m+k]<<" ";
}
outfile<<endl;
}
outfile<<endl;
}
outfile.close();
return 0;
}