软件工程作业03
软件工程 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1 |
---|---|
作业要求 | https://edu.cnblogs.com/campus/zswxy/software-engineering-2017-1/homework/10494 |
作业目标 | 用c++或java编程实现数独填充 |
作业正文 | 见下文 |
参考文献 | 无 |
1、Github项目地址
https://github.com/20177711LBS
2、PSP表格
PSP2.1 Personal | Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 40 |
Estimate | 估计这个任务需要多少时间 | 360 | 1000+ |
Development | 开发 | 120 | 120 |
Analysis | 需求分析 (包括学习新技术) | 100 | 60 |
Design Spec | 生成设计文档 | 50 | 30 |
Design Review | 设计复审 | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 30 |
Design | 具体设计 | 30 | 90 |
Coding | 具体编码 | 120 | 180 |
Code Review | 代码复审 | 60 | 180 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 120 |
Reporting | 报告 | 30 | 60 |
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 10 | 5 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 60 |
合计 | 1040 | 2055 |
3、思路描述
拿到题目的时候其实没有看懂到底要求做什么,对于命令行传入参数也是一无所知,在群里面询问大佬们,了解命令行如何传参之后,才正式开始构思如何求解九宫格盘面,好在自己平时也喜欢玩数独,给我一个九宫格的盘面30分钟不到就能解完,可如今要自己来手写代码,让代码来解读,这到难倒我了,以自己目前的水平和知识面,写完估计的要300分钟吧!废话不多说了,先讲讲自己的思路吧:首先我们得知道3-9宫格最终盘面里每个数字所应满足的要求:
三宫格:盘面是3*3。使1-3每个数字在每一行、每一列中都只出现一次,不考虑宫;
四宫格:盘面是2*2四个宫,每一宫又分为2*2四个小格。使1-4每个数字在每一行、每一列和每一宫中都只出现一次;
五宫格:盘面是5*5。使1-5每个数字在每一行、每一列中都只出现一次,不考虑宫;
六宫格:盘面是2*3六个宫,每一宫又分为3*2六个小格。使1-6每个数字在每一行、每一列和每一宫中都只出现一次;
七宫格:盘面是7*7。使1-7每个数字在每一行、每一列中都只出现一次,不考虑宫;
八宫格:盘面是4*2八个宫,每一宫又分为2*4八个小格。使1-8每个数字在每一行、每一列和每一宫中都只出现一次;
九宫格:盘面是3*3九个宫,每一宫又分为3*3九个小格。使1-9每个数字在每一行、每一列和每一宫中都只出现一次
根据这个要求写一个方法legal,以判断在九宫格中的坐标(x,y)的位置上插入value,是否符合上述规则,代码如下
public static Boolean legal(int a[][],int x, int y, int value,int m) {
for (int i = 0; i < m; i++) {
//如果列中有value,则返回false
if (i != x && a[i][y] == value) {
return false;
}
//如果行中有value,则返回false
if (i != y && a[x][i] == value) {
return false;
}
}
if(m==9){
//(minX,minY)是(x,y)所属小九宫格的左上角的坐标
int minX = x / 3 * 3;
int minY = y / 3 * 3;
for (int i = minX; i < minX + 3; i++) {
for (int j = minY; j < minY + 3; j++) {
//如果小九宫格中的非(x,y)的坐标上的值为value,返回false
if (i != x && j != y && a[i][j] == value) {
return false;
}
}
}
}
if(m==4){
//(minX,minY)是(x,y)所属小4宫格的左上角的坐标
int minX = x / 2 * 2;
int minY = y / 2 * 2;
for (int i = minX; i < minX + 2; i++) {
for (int j = minY; j < minY + 2; j++) {
//如果小九宫格中的非(x,y)的坐标上的值为value,返回false
if (i != x && j != y && a[i][j] == value) {
return false;
}
}
}
}
if(m==8){
//(minX,minY)是(x,y)所属小8宫格的左上角的坐标
int minX = x / 4 * 4;
int minY = y / 2 * 2;
for (int i = minX; i < minX + 4; i++) {
for (int j = minY; j < minY + 2; j++) {
//如果小九宫格中的非(x,y)的坐标上的值为value,返回false
if (i != x && j != y && a[i][j] == value) {
return false;
}
}
}
}
if(m==6){
//(minX,minY)是(x,y)所属小6宫格的左上角的坐标
int minX = x / 2 * 2;
int minY = y / 3 * 3;
for (int i = minX; i < minX + 2; i++) {
for (int j = minY; j < minY + 3; j++) {
//如果小九宫格中的非(x,y)的坐标上的值为value,返回false
if (i != x && j != y && a[i][j] == value) {
return false;
}
}
}
}
return true;
}
legal方法写完之后,并没有结束,求解九宫格的核心思想让我为之思考了一整天,首先想到的是按照平时玩数独的思维来解答:也就是自己常用的排除法,先将每行每列每个宫里面不可能出现的数字排除掉,然后将一些确定的数字填上去,然后再排除,再填......显然这种方法就是没脑子的人才会想的出来的,写完估计都猴年马月了,于是去询问ACM的算法大佬,提示了我一下,让我使用回溯法,刚提完,我瞬间“柳暗花明又一村”,马上有了思路:
public static void shuDu_solution(int k,int m) throws IOException {
if (k == (m*m)) {
String src= "D:\sudoku\"+outputFilename;
try{
FileWriter fw = new FileWriter(src,true);
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
fw.write(shuDu[i][j]+" ");
}
fw.write("
");
}
fw.write("
");
fw.close(); // 最后记得关闭文件
}
catch (Exception e) {
e.printStackTrace();
}
return;
}
int x = k / m;
int y = k % m;
if (shuDu[x][y] == 0) {
for (int i = 1; i <= m; i++) {
shuDu[x][y] = i;
if (legal(shuDu,x, y, i,m)) {
shuDu_solution(k + 1,m);
}
}
shuDu[x][y] = 0;
} else {
shuDu_solution(k + 1,m);
}
}
初始化命令行的传入的参数
public static void loadArgs(String args[]){
if(args.length>0&&args!=null){
for(int i=0;i<args.length;i++){
switch (args[i]) {
case "-i":
inputFilename = args[++i];
break;
case "-o":
outputFilename = args[++i];
break;
case "-m":
m=Integer.valueOf(args[++i]);
break;
case "-n":
n=Integer.valueOf(args[++i]);
break;
default:
break;
}
}
}
}
最后就是主函数
public static void main(String[] args) throws IOException {
loadArgs(args);
int generateShuDu[][]=new int[10][10];
File myFile = new File("D:\sudoku",inputFilename);
Reader reader = new InputStreamReader(new FileInputStream(myFile),"UTF-8");
int tempchar; int i=0; int j=0;
while ((tempchar = reader.read()) != -1) {
if ( (((char) tempchar) != '
') &&(((char) tempchar) != ' ')) {
if(i<m){
if(j<m){
if(tempchar!=13){
generateShuDu[i][j]=((char) tempchar)-48;
j++;
}
}else{
i++;
j=0;
generateShuDu[i][j]=((char) tempchar)-48;
}
}
if(i==m){
if(n!=0){
setShuDu(generateShuDu);
shuDu_solution(0,m);
n--;
i=0;j=0;
}
}
}
}
reader.close();
}
遇到的问题
FileWriter fw = new FileWriter("c.txt",true);
fw.write("hello");
fw.close();
异常处理:主要是对文件的读取进行异常处理
单元测试样例:
最后总结一下:
用java开发感觉if-else流程语句和一些基本语法没有太大的问题,主要是一些常用的类(文件处理类,IO流)使用还不太熟练,导致开发效率低,中途遇到各种各样的bug,以至于气的连晚饭都不想吃了,但自己还是坚持做完了,bug也解决了;除此之外,编辑器的使用也不太熟练,一些类似于命令行传参的细节性的问题也不懂,不过现在懂了,也算是一份收获吧,另外,学会了在github上面上传文件和文件夹,对于github的使用有了初步的认识,希望自己在下次的项目开发中有新的收获,遇到bug不要难过,要勇于挑战bug这样才能不断突破自己!