2020软件工程作业03
软件工程 | |
---|---|
作业要求 | |
作业目标 | |
作业正文 | 如下 |
参考文献 |
1、Github项目地址
https://github.com/Risiblejdd/work/tree/master/src
2、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | |||
Estimate | |||
Development | |||
Analysis | |||
Design Spec | |||
Design Review | |||
Coding Standard | |||
Design | |||
Coding | |||
Code Review | |||
Test | |||
Reporting | |||
Test Repor | |||
Size Measurement | |||
Postmortem & Process Improvement Plan | |||
合计 |
3、解题思路
百度百科简介:
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
这个题目首先想到的就是深搜+回溯的算法。但是对于编程,对于算法,我真的是只会说不会做。所以一看到这次作业我就头疼,但是还是要迎难而上啊啊啊啊啊(哭哭/(ㄒoㄒ)/~~!!!
首先开始阶段,要先初始化数独盘,在题目已经填写的数字基础上对我们要填的格子进行填充限制,如下图所示:
然后是数字填充阶段,回溯法,就是在已有的限制条件下,按顺序尝试1-9在格子中的填充,如果在尝试过程中填写到了数独盘的最后一格,则代表数独已经解出来了(此方法仅限提供的数独盘只有唯一解,如果有多解也只能输出一个),如果到达某格无法继续填充后续数字,需要移除之前放置的数字,然后继续尝试,如下图所示:
4、实现过程
3、4、5、6、7、8、9阶测试
5、改进程序性能
6、关键代码
回溯方法:
/**
* 回溯填充方法
* @param row
* @param col
*/
public void backtrack(int row, int col) {
if(shudoPan[row][col] == 0) {
for(int d = 1; d <= m; d++) {
int idx = 0;
if(boxRow > 0) {
idx = (row / boxRow ) * boxRow + col / boxCol;
}
if(couldPlace(d, row, col)) {
//填充数字,并设置填充限制
boxOccupied[idx][d]++;
rowOccupied[row][d]++;
colOccupied[col][d]++;
shudoPan[row][col] = d;
//是否填充到最后一格
if ((col == m-1) && (row == m-1)) {
sudokuSolved = true;
}
else {
//当到达最后一列的格子,下一个格子跳转到下一行
if (col == m-1) {
backtrack(row + 1, 0);
}else {
backtrack(row, col + 1);
}
}
if(!sudokuSolved) {//移除填充后无法进行后续填充的数
boxOccupied[idx][d]--;
rowOccupied[row][d]--;
colOccupied[col][d]--;
shudoPan[row][col] = 0;
}
}
}
}else {
if ((col == m-1) && (row == m-1)) {
sudokuSolved = true;
}
else {
//当到达最后一列的格子,下一个格子跳转到下一行
if (col == m-1) {
backtrack(row + 1, 0);
}else {
backtrack(row, col + 1);
}
}
}
}
解数独方法---用于初始化和调用回溯方法。
代码中的idx = (i / boxRow ) * boxRow + k / boxCol;是根据m宫格的宫格行列大小boxROW、boxCol来确定待解格子所在宫号(假设宫按顺序从左到右,自上而下标号为0~(idx-1))。
/**
* 解数独方法
*/
public void solveSudoku(int[][] shudoPan) {
setBox();//调用设置宫的行列数方法
//System.out.println("boxRow,boxCol:"+boxRow+" "+boxCol);
// 初始化某数所在行、列、宫
for (int i = 0; i < m; i++) {
for (int k = 0; k < m; k++) {
int num = shudoPan[i][k];
if (num != 0) {
int d = num;
if(boxRow > 0) {
int idx = (i / boxRow ) * boxRow + k / boxCol;
boxOccupied[idx][d]++;
}
rowOccupied[i][d]++;
colOccupied[k][d]++;
}
}
}
backtrack(0, 0);
}
}
设定宫的大小.
如果宫格阶数为3、5、7的话就把代表宫格行列大小的boxRow、boxCol设为-1,用于后面判断使用。这样的好处是实现了对原来9宫格功能的扩展。
/**
* 设定宫的大小
*/
public static void setBox() {
if(m == 4) {
boxRow = 2;
boxCol = 2;
}
if(m == 6) {
boxRow = 2;
boxCol = 3;
}
if(m == 8) {
boxRow = 4;
boxCol = 2;
}
if(m == 9) {
boxRow = 3;
boxCol = 3;
}
if(m == 3 || m == 5 || m == 7) {
boxRow = -1;
}
}
对文件中读取到的数据,取得其中第numb个数独盘并进行解数独。
其中包含三行
Initialize(rowOccupied);
Initialize(colOccupied);
Initialize(boxOccupied);
是个循环初始化占位数组方法Initialize,用于计算完一个数独盘后将用来标记占位的三个数组重新归0;
/**
* 取得第numb个数独盘并进行解数独
* @param numb
* @param m
*/
public void getAndDO(int numb) {
int index;
for(int i = 0; i < m; i++) {
index = numb*m+i;
Slipt(hang.get(index));
}
//将三个判断占位的数组初始化为0,把判断数独是否解完初始化为false
Initialize(rowOccupied);
Initialize(colOccupied);
Initialize(boxOccupied);
sudokuSolved = false;
solveSudoku(shudoPan);
j = 0;
}
7、心路历程与收获
写到这里我实在是头昏眼花了,为了此次作业,这两周除了上课我每天从早到晚都坐在电脑前,各种百度查资料,然后还是很多借鉴了别人的,才勉强提交了作业。从什么时候开始我感觉自己不适合编程呢,大概是从大二的时候。入学以来,无论上什么课我都十分积极认真,所以我的成绩也一直是班级第一。也许其他同学会觉得我厉害,但只有我自己知道我的优势只在数学,只在理论,只在想法。大一有好几门和数学相关的学科,对数学极其敏感的我轻而易举地就取得了班级第一。然后大二我们开始更多地上专业课,我上课还是那么认真,课后也会去自学。因为很喜欢李津老师教的Java,我决定把Java当作自己擅长的语言,我不仅用Java完成作业,也在洛谷和pta上疯狂刷题。当然我是有进步的,但是很快我就遇到了新的问题,那就是算法。无论用什么样的语言,算法是避免不了的。由于我对数学的敏感,其实很多时候我总是很快就能想到解决问题的方法,但是让我去用编程实现,我就像是瞬间丧失了自己所会的一切。明明大家都说,想要编程好数学一定要好,可是我感觉自己数学还算不错,但编程真的没法说。之前的数据结构、操作系统、算法分析,每次有那种团队实验作业,我的想法都是最快出来的的那个,但编程能力去真的差到不行。我不敢说我上课是最认真的那个,但是我一定是上所有课都认真的那个,给我们上过课的老师们都知道。我也有课后去各种网站自学钻研,各种刷题。可我一直找不到提升自己编程能力的方法,然后这次的作业让我更加困惑于此。说这么多,我只是想表明我真的在努力去做好。两周的时间,让我收获了什么呢?让我更看清了自己的优势劣势,让我更清楚了自己的坐标,我在想是不是要重新定位一下自己的目标。我还是先去做一下其他科目的作业⑧,为了这个囤了好多其他作业QAQ。
8、评分