软件工程 |
|
---|---|
作业要求 |
|
作业目标 |
|
作业正文 | 如下 |
参考文献 |
|
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、评分