>>>;<<
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 15 |
Estimate | 估计这个任务需要多少时间 | 15 | 15 |
Development | 开发 | 320 | 660 |
Analysis | 需求分析 (包括学习新技术) | 20 | 120 |
Design Spec | 生成设计文档 | 0 | 0 |
Design Review | 设计复审 | 30 | 0 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 0 | 0 |
Design | 具体设计 | 20 | 120 |
Coding | 具体编码 | 120 | 150 |
Code Review | 代码复审 | 10 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 240 |
Reporting | 报告 | 160 | 270 |
Test Report | 测试报告 | 20 | 120 |
Size Measurement | 计算工作量 | 20 | 120 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 120 | 30 |
合计 | 495 | 945 |
看到题目是数独的时候,我大脑里第一反应是,游戏,数学家没事时玩的,一张的纸,一支铅笔,擦擦写写。好吧,这个游戏我听说过,但是从来没玩过,所以做的第一件事,在手机上装个数独游戏玩玩。这里我推荐“数独专业版”app,没有广告,界面简洁,小米商店评分4.9,下载来体验一把做数学家的惊险与刺激,简直是不二选择。边玩边思考,玩了两盘,灵感就来了。
首先,解一个数独题,其实就三步走:
第一,是最基本的,要知道哪些格子里有数,哪些格子没数。
第二,是最关键的,没数的格子里可以填哪些数。
第三,是最重要的,如何把格子填满。
第一步,用一个很简单的if语句就可以判断出哪些格子有没有数,没有数的要记录下来。我把它们放到一个队列里,排队等候填数
for (int i = 0; i != max; ++i){number[i] = new int[max];for (int j = 0; j != max; ++j){number[i][j] = array[i][j]; //这里其实是Koe(宫格)类的初始化过程if (number[i][j] == 0)space_x.push(i), space_y.push(j); //顺便找一下待处理的格子,将其坐标存入队列spaceelse if (divided)block[int(i*div_x)][int(j*div_y)][number[i][j]] = 1;//划分块,没宫的用不着}}
第二步,如果一个格子没数,如何得到它能填的数呢?从游戏规则上来讲是横竖不重复,分块内不重复。那就得从已存在的数入手
void Koe::available(int i, int j, queue<int> &rest) //找寻i行j列元素可用数,存放在rest队列
{int m = 0, max_ = max + 1;int *exist = new int[max_]; //因为要以存在数的值作为下标,所以得多开一点空间while (m != max_)exist[m++] = 0;for (m = 0; m != max; ++m){exist[number[i][m]] = 1; //横竖同时判断,一个循环搞定exist[number[m][j]] = 1; //不用跳过自己,反正必定有number[i][j]=0,再加判断只是空耗开销}if (divided) //从分块里再看{int x = int(i *div_x), y = int(j *div_y); //用i,j乘以分块划分比,即可得出i,j所在分块下标for (int z = 1; z != max_; ++z){if (block[x][y][z] == 1)exist[z] = 1; //block[x][y][z]=1的意思是,分块[x][y]中存在数字z}}m = 1; //从1开始记录while (m != max_){if (exist[m] == 0)rest.push(m); //不存在的放入可用队列rest++m;}delete []exist; //new出来的数组可以删了exist=NULL;
}
第三步,怎么把格子填满?我用的是递归的方法,逐个处理在Koe初始化的时候,我已经把空位存入了队列space,所以我只要一个一个取出来填就行了,填什么?上面的的available方法已经给了答案(以下源码经过简化)
int Koe::deduce(queue<int>s_x,queue<int>s_y)
{int x = s_x.front(), y = s_y.front();queue<int> rest;available(x, y, rest); //就当前位置找可用数字s_x.pop(), s_y.pop();int blk_x = int(x *div_x), blk_y = int(y * div_y);int record = number[x][y]; //记录当前数字,保存现场。很没必要,我知道它一定是0int answer = 0;while (!pty()) //可用数字不为空,就一直找下去{number[x][y] = rest.front(); //填一个数字rest.pop();if (divided)block[blk_x][blk_y][number[x][y]] = 1; //填好数字后相应的分块里要置1,表示占用if (!pty())answer += deduce(s_x, s_y); //进入下一阶填空......if (divided)block[blk_x][blk_y][number[x][y]] = 0; //分块内数字取消占用}if (pty()) //空填完了,即找到了答案{......}number[x][y] = record; //恢复数字,等于0即可return answer;
}
整个项目,只有Koe一个类,里面装的有点多,是项目的主体,结构如下
很多人会这觉得,解出数独即是完成了这次作业。解数独确实是这个项目的主要需求,也是整个项目构建起手之处,当然不排除有些人先构建IO啦。我是从解数独Koe类开始的,但是从新建一个项目到屏幕上可以正确输出只过了40分钟,准确地说43分钟,比我预想的要快得多。我想了想,我做完了吗?没有。停下来思考一下,总体完成度大概在66%左右。
这次作业还有一个关键点在于——对命令行的输入处理
并且,在作业要求里也有明确提到,对于错误的处理。我想了想,对于错误处理,在内部数据结构正确的情况下,出错基本是因为对输入处理得不够严谨,用正确的算法处理错误的数据,当然不会得到正确得结果。
我的目标是:对于任何输入,我的程序都能够有相应的反应。
错误处理考虑:
对于输入的处理我还不敢保证绝对完美,毕竟我个人的精力和脑回路是有限的,我暂时已经想不出还会有其他我没考虑到情况了。实际上我可能已经过度考虑了:虽然atoi函数只能返回int型,但要是阶数盘数输入出现小数我也会输出警告。此时,虽然程序已经获得到了可用有效的信息,但是我还是遗憾地选择终止进程,因为输入不符合规范,规范是整数。比如-m 3.8,我用atoi转换一下只能得到3,但是在*argv[]里我仍然能找到一个'.',基于人性化考虑我可以让它以3接着运行下去;基于严谨性我认为我获得了一个有效信息3,可是这个有效信息3和用户输入的原始信息3.8所表达的信息有出入。我该如何在这两者之间选择或者权衡?我最终还是选择了严谨。
再到头来看,可能会有人觉得可笑了,因为用户输入小数的可能性很小啊,用户自己是想输入整数的,他也清楚自己要是输入小数那绝对也不对,总之输入的可能性超级小,而且还有atoi替我处理......好吧,不讨论这个了,越讨论越觉得这个处理没必要。
在写博客的过程中,我能想到很多在之前没有想到的事。
代码分析还是不会用,就算用出来了我也看不懂,以下,多图警告!(→ܫ→)
递归算法,算是十分形象了
基本测试:
拓展测试:
转载于:.html
本文发布于:2024-02-04 09:32:39,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170704176454402.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |