现代程序设计 homework-01
搞了6个小时individual project...看看博客做一做第一次现代程序设计作业
1) 建立 GitHub 账户, 把课上做的 “最大子数组之和” 程序签入
我的github地址是https://github.com/oldoldb,以前没有用过各种不熟练啊....代码我放到<现代程序设计课后作业>这个repository里了,是昨天完成的在hdu和poj上找的5道关于一维和二维情况的最大子数组&最大子矩阵问题的练习,具体的解题报告参见第一篇博客:http://www.cnblogs.com/oldoldb/p/3312195.html
2) 在 cnblogs.com 建立自己的博客。 写博客介绍自己的 GitHub 账户. 并把博客地址写到这个博客的留言。这样TA 可以收集信息
博客地址是http://www.cnblogs.com/oldoldb
3) 搞到一本教科书 (三本中选一本), 并在博客中说明自己选的是哪一本。
在csdn和新浪爱问上搞到了三本书的电子版...回头大体浏览一下再决定将哪本书作为教科书吧...吐槽一下移山之道没有找到免费的电子版。。。好吧我尊重原创
PS:今天《移山之道》,感觉。。。1是好多开发方法开发工具什么的我不用现在也看不懂。。 2是这本书。。其实真的不太适合做教科书吧。。说真的。。。那些打趣的东西完全没有必要的。。。改天浏览一下其他两本书。。
4) 阅读下面的博客:
- 个人软件开发流程: Personal Software Process,
- 程序效能分析
- 单元测试 (在最小的编程单元上保证正确性) & 回归测试 (保证程序在修改的过程中, 原有的功能保持稳定 )
- 技能的反面
大体读了一下。。虽然软件开发的很多东西自己根本不懂。。平时的编程也仅限于算法和数据结构方面。。。
5) 在自己的博客上描述自己是怎么设计 “最大子数组之和”这个程序的, 和正确的解法有哪些差距。
同第一条,具体的解题报告参见第一篇博客:http://www.cnblogs.com/oldoldb/p/3312195.html
为了
邹老师的第一门课上小测了最大子矩阵的问题,课堂上只想到了O(n^2*m^2)的算法
后来翻《编程之美》的时候翻到了这道题...今天下午研究了一下,到OJ上切了几道题,熟悉了动态规划在这个问题中的应用.
一.最大子段和
书上已经将这种思想讲的很透彻,对基本的最大子段和的练习可以参照hdu1003题http://acm.hdu.edu.cn/showproblem.php?pid=1003,唯一的一点变形是这道题要求求出最大字段的起始位置和终止位置.
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #define INF 0x3f3f3f3f 7 using namespace std; 8 const int maxn=100005; 9 int map[maxn]; 10 int ans; 11 int begin; 12 int end; 13 int n; 14 void getmap() 15 { 16 for(int i=1;i<=n;i++) 17 { 18 cin>>map[i]; 19 } 20 } 21 void work() 22 { 23 int tempsum=0; 24 int tmpbegin=1; 25 for(int i=1;i<=n;i++) 26 { 27 tempsum+=map[i]; 28 if(tempsum>ans) 29 { 30 ans=tempsum; 31 begin=tmpbegin; 32 end=i; 33 } 34 if(tempsum<0) 35 { 36 tempsum=0; 37 tmpbegin=i+1; 38 } 39 40 } 41 } 42 43 int main() 44 { 45 int T; 46 cin>>T; 47 for(int tt=1;tt<=T;tt++) 48 { 49 ans=-INF; 50 cin>>n; 51 getmap(); 52 work(); 53 cout<<"Case "<<tt<<":"<<endl; 54 cout<<ans<<" "<<begin<<" "<<end<<endl; 55 if(tt<T) 56 { 57 cout<<endl; 58 } 59 } 60 return 0; 61 }
关于最大子段和有一种变形是最大m子段和,http://acm.hdu.edu.cn/showproblem.php?pid=1024,就是在原有的基础上求m段子段的和,这里建立动态规划方程,dp[i][j]表示前j个数(包括第j个数)组成i段的和的最大值,那么根据第j个数是否独立成组得到转移方程为dp[i][j]=max(dp[i-1][j-1]+a[j],dp[i-1][k]+a[j]),0<k<j,这里dp[i][]只和dp[i-1][]有关,用一维数组就可以表示,同时dp[i-1][k]表示1~j-1中构成i-1组的最大值,即前一组的值,那么可以在循环中求出,所以复杂度为O(n*m)
1 /* 2 dp[i][j]前j个数(包括第j个数,组成i组的和的最大值 3 dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j 4 即根据第j个数是否单独成组划分状态 5 前i组仅与前i-1组有关,这里用一维数组就好 6 而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值 7 */ 8 #include<iostream> 9 #include<cstdio> 10 #include<cstring> 11 #include<cstdlib> 12 #include<algorithm> 13 #define INF 0x3f3f3f3f 14 using namespace std; 15 const int maxn=1000005; 16 __int64 map[maxn]; 17 __int64 dp[maxn]; 18 __int64 prev[maxn]; 19 __int64 ans; 20 int m; 21 int n; 22 void getmap() 23 { 24 dp[0]=0; 25 prev[0]=0; 26 for(int i=1;i<=n;i++) 27 { 28 scanf("%I64d",&map[i]); 29 dp[i]=0; 30 prev[i]=0; 31 } 32 } 33 void work() 34 { 35 for(int i=1;i<=m;i++) 36 { 37 ans=-INF; 38 for(int j=i;j<=n;j++) 39 { 40 dp[j]=max(dp[j-1],prev[j-1])+map[j]; 41 prev[j-1]=ans; 42 ans=max(ans,dp[j]); 43 } 44 } 45 } 46 int main() 47 { 48 while(scanf("%d%d",&m,&n)!=EOF) 49 { 50 getmap(); 51 work(); 52 printf("%I64d\n",ans); 53 } 54 return 0; 55 }
根据这道题那么poj2593,http://poj.org/problem?id=2593,就很容易解决了...就是将m替换为两段就好
1 /* 2 dp[i][j]前j个数(包括第j个数,组成i组的和的最大值 3 dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j 4 即根据第j个数是否单独成组划分状态 5 前i组仅与前i-1组有关,这里用一维数组就好 6 而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值 7 */ 8 #include<iostream> 9 #include<cstdio> 10 #include<cstring> 11 #include<cstdlib> 12 #include<algorithm> 13 #define INF 0x3f3f3f3f 14 using namespace std; 15 const int maxn=1000005; 16 int map[maxn]; 17 int dp[maxn]; 18 int prev[maxn]; 19 int ans; 20 int m; 21 int n; 22 void getmap() 23 { 24 dp[0]=0; 25 prev[0]=0; 26 for(int i=1;i<=n;i++) 27 { 28 scanf("%d",&map[i]); 29 dp[i]=0; 30 prev[i]=0; 31 } 32 } 33 void work() 34 { 35 for(int i=1;i<=m;i++) 36 { 37 ans=-INF; 38 for(int j=i;j<=n;j++) 39 { 40 dp[j]=max(dp[j-1],prev[j-1])+map[j]; 41 prev[j-1]=ans; 42 ans=max(ans,dp[j]); 43 } 44 } 45 } 46 int main() 47 { 48 while(scanf("%d",&n)!=EOF&&n) 49 { 50 m=2; 51 getmap(); 52 work(); 53 printf("%d\n",ans); 54 } 55 return 0; 56 }
二.最大子矩阵
最大子矩阵的算法就是将第k列中第i行到j行的数的和求出,然后套用一维的算法就好
我拿poj1050,http://poj.org/problem?id=1050,做了练习
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #define INF 0x3f3f3f3f 7 using namespace std; 8 const int maxn=105; 9 int map[maxn][maxn]; 10 int sum[maxn][maxn]; 11 int ans; 12 int n; 13 void getmap() 14 { 15 for(int i=1;i<=n;i++) 16 { 17 for(int j=1;j<=n;j++) 18 { 19 cin>>map[i][j]; 20 } 21 } 22 } 23 void getsum() 24 { 25 memset(sum,0,sizeof(sum)); 26 for(int i=1;i<=n;i++) 27 { 28 for(int j=1;j<=n;j++) 29 { 30 sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j]; 31 } 32 } 33 } 34 void work() 35 { 36 ans=-INF; 37 for(int i=1;i<=n;i++) 38 { 39 for(int j=i;j<=n;j++) 40 { 41 int start=sum[j][n]-sum[i-1][n]-sum[j][n-1]+sum[i-1][n-1]; 42 int all=start; 43 for(int k=n-1;k>=1;k--) 44 { 45 if(start<0) 46 { 47 start=0; 48 } 49 start+=sum[j][k]-sum[i-1][k]-sum[j][k-1]+sum[i-1][k-1]; 50 all=max(all,start); 51 } 52 ans=max(ans,all); 53 } 54 } 55 } 56 int main() 57 { 58 while(cin>>n) 59 { 60 getmap(); 61 getsum(); 62 work(); 63 cout<<ans<<endl; 64 } 65 return 0; 66 }
暂时只能想到这些,以后遇到该问题的变形,比如像课后扩展那样对于矩阵可以首尾相连,二维扩展到三维、四维等等的问题,我会继续在这里更新。
关于一些作业要求:
程序的架构和思路:就是一个dp,dp[i]为前i个数的最大和(当然这里因为参照了《编程之美》写的是从后向前dp,不过思想都是一样的,在第二次作业中会用从前向后dp),状态转移方程为dp[i]=max{dp[i-1]+a[i],a[i]}
心得:心得就是《编程之美》这本书上关于最大子数组的O(n)解法其实饶了一个弯。。并不是那么简洁。。完全可以直接从前向后dp
时间消耗:这个问题比较简单,用了一个下午的时间来写代码+找题目+初步写blog,现在在按照作业要求修改blog
开发效率分析:这个。。。。是什么意思。。。复杂度是O(n),还有么。。。
运行结果:在github上有详细的测试数据和代码
PS:关于这部分的代码我在第二次作业一开始对于简单最大子数组问题的复习中改写了一下。。。可惜被不小心删掉了。。。貌似不能通过数据恢复恢复了?我改天做第二次作业的时候再完善一下。。