homework-02
这次作业的复杂程度真是超越了以往的任何一次大作业啊(不要问我为什么……),总之开始叙述吧……(因为过程比较多,所以签入的程序也比较多,不过都是包括homework的,例如homework-02,homework-02(2),等等,因为实在不知道要怎么命名了)
(1)
这个阶段就是把第一次作业实现,需要增加的部分是文件系统读入,至于输出程序,采用控制台输出。(……)
代码如下(就是记事本写的,没办法,电脑一运行VISUAL STUDIO就卡的不行):
![](http://images0.cnblogs.com/blog/568497/201309/29211804-b0bd44e060494fd38ff45e6813caaa3b.png)
这是问题2的子函数,直接截图过来了。如上是一维问题的处理。然而我们的第二步工作开始复杂起来了 ~
(2)处理二维数组。这部分网上找了不少资料,选取了一个复杂度较低的版本,理解也很简单,下面叙述一下吧!
输出仍然是控制台输出(……),这里我很无奈的限制了数组的大小,没错,这种方法对数组的大小限制很高,因为需要设立数组保存中间变量。
/*
解法:
仍然用动态规划的思想。
首先,将二维问题降维处理:
例如,用2 维数组a[1 : m][1 : n]表示给定的m行n列的整数矩阵。
子数组a[i1 : i2][j1 : j2]表示左上角和右下角行列坐标分别为(i1, j1)和(i2, j2)的子矩阵。
先按照行排列出所有可能区间,然后,再去求列的范围。
更详细的,当行区间确定之后,剩下就是确定列区间了,一旦确定列区间,最大子矩阵就确定了。
当行区间确定之后,求列区间的方法,可以转化成一维数组的最大连续子序列的问题:对行区间[i1, j1],
依次对列进行求和,就得到n个数据的以为数组,根据最大连续子序列的和的求法,就可以获得连续子序列最大和。
仍然用nGreatestNum来记录最大值,算出一个子矩阵的和,就进行比较即可。
复杂度分析:
(1)排列出行区间,复杂度为O(M*M);
(2)而求得最大子序列的和复杂度为O(N);
(3)对于行区间确定之后对列求和的复杂度呢?
这里采用“部分和”的做法。
用BC[i][j]表示0到i行、0到j列的总和。
那么对于行区间r->l,求第i列的和:BC[l][i] - B[r-1][i] - B[l][i-1] + B[r-1][i-1]。
而求“部分和”仅需要O(N*M)。可以预先计算好。
因此,算法复杂度为O(N*M*M)。
*/
解法:
仍然用动态规划的思想。
首先,将二维问题降维处理:
例如,用2 维数组a[1 : m][1 : n]表示给定的m行n列的整数矩阵。
子数组a[i1 : i2][j1 : j2]表示左上角和右下角行列坐标分别为(i1, j1)和(i2, j2)的子矩阵。
先按照行排列出所有可能区间,然后,再去求列的范围。
更详细的,当行区间确定之后,剩下就是确定列区间了,一旦确定列区间,最大子矩阵就确定了。
当行区间确定之后,求列区间的方法,可以转化成一维数组的最大连续子序列的问题:对行区间[i1, j1],
依次对列进行求和,就得到n个数据的以为数组,根据最大连续子序列的和的求法,就可以获得连续子序列最大和。
仍然用nGreatestNum来记录最大值,算出一个子矩阵的和,就进行比较即可。
复杂度分析:
(1)排列出行区间,复杂度为O(M*M);
(2)而求得最大子序列的和复杂度为O(N);
(3)对于行区间确定之后对列求和的复杂度呢?
这里采用“部分和”的做法。
用BC[i][j]表示0到i行、0到j列的总和。
那么对于行区间r->l,求第i列的和:BC[l][i] - B[r-1][i] - B[l][i-1] + B[r-1][i-1]。
而求“部分和”仅需要O(N*M)。可以预先计算好。
因此,算法复杂度为O(N*M*M)。
*/
代码如下 :
![](http://images0.cnblogs.com/blog/568497/201309/29212830-76fa7c1202384ed2ae23486a91a988bc.png)
![](http://images0.cnblogs.com/blog/568497/201309/29212708-49ff6ad2e7d241539a2f7e0d0af53217.png)
(3)问题3的处理要麻烦很多。首先我们想到的仍然是动态规划,以及继承已有的工作。想到二维数组矩形子集的处理方法,如果只要求联通,情况就要自由许多,如果我们仍然局限于将最后选中的元素框起来——恐怕是复杂得多。我想,可不可以采用标志法,即,我们首先对每一行元素用一维数组的处理方法进行处理,对选中的元素进行标示。之后我们想办法链接这些元素,不排除有一些元素链接的代价要远远超过元素本身,因此可能会放弃—— 当然,还有一部分正数,他们没有被标示,但是仍然可以通过某种有效的链接对整体产生积极影响。因此我们要对所有的正数进行“摸排”。
初始标示:对每一行元素进行处理,求出最大子集的和,并记录上下边界,在选中的元素上做标记(可另外设置一个数组存放状态)
链接:从第零行开始,依次比较每一行元素与对应下一行元素,如果恰好有一对元素都被标志,那么意味着这两行元素能链接,继续下一行。如果不幸,这两行元素没有任何一对标志元素在同一列,那只好查找链接:并且选择代价最少的链接方式。若不能找到代价合理的链接,放弃即可。
正数摸排:查找那些没有被标志的正数,尽可能找到一种代价小于此正数值的链接,并加以标志。若不能找到该链接,放弃这个正数。
最后,进行整理。能够链接都已经做好,我们开始对那些没有加入联通回路的链接进行取消标志的操作。最后,将已经标志的数组元素加起来,得到最大的联通元素和。
贴代码:
(……可能还没写完,概要)
![](http://images0.cnblogs.com/blog/568497/201309/29220806-ffb5dd2a53ac4b3cb3f27e7f48f5effd.png)
![](http://images0.cnblogs.com/blog/568497/201309/29220900-62e5d39147fa4d25946f6e4acd5ef8d1.png)
(4)这个阶段要求可以左右相连或者上下相连,前提条件是二维数组,难道还要相互连通?嗯……仍然从一维数组开始考虑,假设一维数组是可以左右联通的,那么我们你能够得到代码如下:
![](http://images0.cnblogs.com/blog/568497/201309/30182250-3ab123e2e81c4d52b86055ecd4523af6.png)
![](http://images0.cnblogs.com/blog/568497/201309/30182535-d232646b6207436381293f211f2b8e87.png)
这个一维数组的做法是,标记子数组的首末字符,然后根据情况进行范围内第二次扫描。
如果是二维数组呢?
二维数组(矩形)也采用类似的办法。因为之前的矩形数组已经做出来了,所以在此基础上扩展即可。
![](http://images0.cnblogs.com/blog/568497/201309/30190043-439f3ec5172649a4bec47a6a6d11c023.png)
![](http://images0.cnblogs.com/blog/568497/201309/30190125-2550bf09a47b4c019eb13dc02c41ee1a.png)
![](http://images0.cnblogs.com/blog/568497/201309/30190214-e273a99d7be341d08097b3f5a8ac0460.png)
上面是横向。那么竖向呢?不过是修改一下m,n就可以了。代码相似,如下:
![](http://images0.cnblogs.com/blog/568497/201309/30192204-a65e3f15ea63451e9d6b9d0a3cdb083c.png)
![](http://images0.cnblogs.com/blog/568497/201309/30192438-408a8a2951614f22908b7e573db6c056.png)
![](http://images0.cnblogs.com/blog/568497/201309/30192512-9b65af73533447ac856c795ad2390a83.png)
(5)这个阶段要求是同时允许两个方向联通,这,思来想去,各个击破?