homework-02
GitHub挂了...我会在其恢复的时候上传我的代码的...其实代码就在blog里面...
已解决...是我自己的问题...之前没有pull一下...结果就上传不上去...
一. 程序的架构和思路:
其实吧...这个第二次作业是在第一次作业上的延伸,于是算法应该和第一次的相似,或者说是将二维转化为一维,然后再做...
但还是从朴素算法开始想吧...
最最朴素的算法,就是一个六重循环,枚举矩阵的左上和右下两个点的x、y坐标,然后计算这之中的值的和即可。
#include<iostream> #include<cstdio> #include<time.h> using namespace std; int a[1010][1010]; int main() { freopen("input.txt","r",stdin); clock_t start,finish; start = clock(); double totaltime; int m,n; int i,j,p,q,k,l,ans,temp = 0; scanf("%d,",&m); scanf("%d,",&n); for (i = 0;i < m;i++) for (j = 0;j < n;j++) scanf("%d,",&a[i][j]); ans = a[0][0]; for (i = 0;i < m;i++) for (j = 0;j < n;j++) for (p = i;p < m;p++) for (q = j;q < n;q++) { temp = 0; for (k = i;k <= p;k++) for (l = j;l <= q;l++) temp = temp + a[k][l]; if (temp > ans) ans = temp; } printf("%d\n",ans); finish = clock(); totaltime = finish - start; printf("%lf\n",totaltime); fclose(stdin); return 0; }
所以该继续优化了...
由代码观之,朴素算法时间复杂度为O(n^6)...这个...太大了有点...
但是,其空间复杂度为1,根据动态规划的基本思想——空间换时间来优化算法。
先从计算两点间矩阵的和下手。
我们可以如此优化:记b[i][j]为左上角到(i,j)的矩阵的和。
因此,(i,j)到(m,n)的矩阵的和就可以表示为:b[m][n]-b[m][j-1]-b[i-1][n]+b[i-1][j-1]。
#include<iostream> #include<cstdio> #include<time.h> using namespace std; int a[1010][1010]; int b[1010][1010]; int main() { freopen("input.txt","r",stdin); clock_t start,finish; start = clock(); double totaltime; int m,n; int i,j,p,q,ans,temp = 0; scanf("%d,",&m); scanf("%d,",&n); for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) scanf("%d,",&a[i][j]); for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) { temp = 0; for (p = 1;p <= i;p++) for (q = 1;q <= j;q++) temp = temp + a[p][q]; b[i][j] = temp; } ans = a[1][1]; for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) for (p = i;p <= m;p++) for (q = j;q <= n;q++) { temp = b[p][q] - b[p][j - 1] - b[i - 1][q] + b[i - 1][j - 1]; if (temp > ans) ans = temp; } printf("%d\n",ans); finish = clock(); totaltime = finish - start; printf("%lf\n",totaltime); fclose(stdin); return 0; }
优化后,算法时间复杂度为O(n^4),空间复杂度为O(n^2)。
需要注意的是,这么做读入数据时要从1开始不要从0开始,因为会有i-1的情况,要保证其大于等于0。
貌似...O(n^4)还是有点大...
秉承空间换时间的基本方针,可以继续优化。
这次就需要基于第一次作业了。
扩大空间,我们用b[i][j][k]表示第k列的第i行到第j行的和,于是我们将其转化为了b[i][j][0],b[i][j][1],...,b[i][j][k]这样一个一维数列
于是就可以求其最大子序列和了。
#include<iostream> #include<cstdio> #include<time.h> #include<cstring> using namespace std; int a[1010][1010]; int b[101][101][101]; int max(int x,int y) { if (x > y) return x; else return y; } int main() { freopen("input.txt","r",stdin); clock_t start,finish; start = clock(); double totaltime; int m,n; int i,j,k,ans,temp = 0; scanf("%d,",&m); scanf("%d,",&n); for (i = 0;i < m;i++) for (j = 0;j < n;j++) scanf("%d,",&a[i][j]); ans = a[0][0]; for (i = 0;i < m;i++) for (j = 0;j < n;j++) ans = max(ans,a[i][j]); memset(b,0,sizeof(b)); for (k = 0;k < n;k++) { for (i = 0;i < m;i++) { temp = 0; for (j = i;j < m;j++) { temp = temp + a[j][k]; b[i][j][k] = temp; } } } for (i = 0;i < m;i++) for (j = i;j < m;j++) { temp = 0; for (k = 0;k < n;k++) { temp = temp + b[i][j][k]; if (temp > ans) ans = temp; else if (temp < 0) temp = 0; } } printf("%d\n",ans); finish = clock(); totaltime = finish - start; printf("%lf\n",totaltime); fclose(stdin); return 0; }
这样时间复杂度就优化到O(n^3)了,可以解决较大矩阵了,但是,空间复杂度同样到达了O(n^3),可能会爆内存。
根据第一次优化的方法,我们可以继续优化空间复杂度。
用b[i][k]-b[j-1][k]来表示b[i][j][k]。
#include<iostream> #include<cstdio> #include<time.h> #include<cstring> using namespace std; int a[1010][1010]; int b[1010][1010]; int max(int x,int y) { if (x > y) return x; else return y; } int main() { freopen("input.txt","r",stdin); clock_t start,finish; start = clock(); double totaltime; int m,n; int i,j,k,ans,temp = 0; scanf("%d,",&m); scanf("%d,",&n); for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) scanf("%d,",&a[i][j]); ans = a[1][1]; for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) ans = max(ans,a[i][j]); memset(b,0,sizeof(b)); for (j = 1;j <= n;j++) { temp = 0; for (i = 1;i <= m;i++) { temp = temp + a[i][j]; b[i][j] = temp; } } for (i = 1;i <= m;i++) for (j = i;j <= m;j++) { temp = 0; for (k = 1;k <= n;k++) { temp = temp + b[j][k] - b[i - 1][k]; if (temp > ans) ans = temp; else if (temp < 0) temp = 0; } } printf("%d\n",ans); finish = clock(); totaltime = finish - start; printf("%lf\n",totaltime); fclose(stdin); return 0; }
这样,时间复杂度不变,空间复杂度变为了O(n^2),但同第二个算法,读入数据时要从1开始不要从0开始。
先跳过不规则连通子图...
对于上下连通的情况,可以在正下方补一个相同的矩阵,这样在两个矩阵中可以实现上下连通的情况。
需要注意的是这样要求第j行减去第i行要小于等于矩阵的宽。
#include<iostream> #include<cstdio> #include<time.h> #include<cstring> using namespace std; int a[1010][1010]; int b[1010][1010]; int max(int x,int y) { if (x > y) return x; else return y; } int main() { freopen("input.txt","r",stdin); clock_t start,finish; start = clock(); double totaltime; int m,n; int i,j,k,ans,temp = 0; scanf("%d,",&m); scanf("%d,",&n); for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) { scanf("%d,",&a[i][j]); a[i + m][j] = a[i][j]; } ans = a[1][1]; for (i = 1;i <= m;i++) for (j = 1;j <= n;j++) ans = max(ans,a[i][j]); memset(b,0,sizeof(b)); for (j = 1;j <= n;j++) { temp = 0; for (i = 1;i <= m * 2;i++) { temp = temp + a[i][j]; b[i][j] = temp; } } for (i = 1;i <= m;i++) for (j = i;j <= m;j++) { if ((j - i) <= m) { temp = 0; for (k = 1;k <= n;k++) { temp = temp + b[j][k] - b[i - 1][k]; if (temp > ans) ans = temp; else if (temp < 0) temp = 0; } } } printf("%d\n",ans); finish = clock(); totaltime = finish - start; printf("%lf\n",totaltime); fclose(stdin); return 0; }
对于左右连通的情况,同样需要向右补一个相同的矩阵,然后并保证所选子矩阵的长不大于原矩阵即可。
但是,从左向右做记录的时候需要另开一个变量记录长度。
实在是懒得写了...索性把上下连通的代码的长宽对调,让其从上往下做,完美解决。
不规则的连通子图,想了想还是没有特别好的办法能够解决32*32的数据。
最朴素的算法就是枚举每个点的状态,用0和1表示。
然后dfs判断连通性并找最大子块,这样的时间复杂度是O(2^(m*n)),就是10*10可解,32*32不可解。
由此想到是否可用状态压缩动态规划做,但是似乎0、1两个状态不够,还需要表示上下连通等其他状态并且似乎转移的时候也有问题。
于是就要用0,1,...,k来表示状态,稍微算了一下时间复杂度,已经是个天文数字了...
于是这一问我实在是想不出什么好的算法了...
二. 程序运行截图:
自上至下依次为第二至第五个算法的运行结果(第一个O(n^6)的实在是跑不出来了...数据是100*100的):
由此可见,速度的提升是极其明显的。
三. 回答问题:
描述在这么多相似的需求面前, 你怎么维护你的设计 (父类/子类/基类, UML, 设计模式, 或者其它方法) 让整个程序的架构不至于崩溃的?
答:我目前是把每个需求分开来写的...如果要合并的话,也许利用case语句能达到要求。
给出你做单元测试/代码覆盖率的最终覆盖率的报告, 用截屏显示你的代码覆盖率.
答:我写的程序是面向过程的...貌似没法做单元测试么?
你在这个作业中学到了什么? 有什么好的设计值得分享? 感想如何 (太容易 / 太难 / 太无趣)?
答:我尝试去学习状态压缩动态规划,但还是不会做那一问...值得分享的是关于多维问题,可以由简单推复杂,即由一维推多维。
说实话...那个不规则的最大子图那一问有点难...
Personal Software Process Stages |
时间百分比(%) |
实际花费的时间 (分钟) |
原来估计的时间 (分钟) |
|
Planning |
计划 |
|||
· Estimate |
· 估计这个任务需要多少时间,把工作细化并大致排序 |
4.8% | 30 | 30 |
Development |
开发 |
|||
· Analysis |
· 需求分析 (包括学习新技术) |
9.6% | 60 | 30 |
· Design Spec |
· 生成设计文档 |
2.4% | 15 | 10 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
0% | 0 | 0 |
· Coding Standard |
· 代码规范 (制定合适的规范) |
1.6% | 10 | 10 |
· Design |
· 具体设计 |
9.6% | 60 | 80 |
· Coding |
· 具体编码 |
38.4% | 240 | 150 |
· Code Review |
· 代码复审 |
19.2% | 120 | 50 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
6.4% | 40 | 60 |
Reporting |
总结报告 |
|||
|
3.2% | 20 | 30 | |
|
4.8% | 30 | 30 | |
Total | 总计 | 100% |
总用时 625 |
总估计的用时 470 |