【神一样的作业】二维数组连续的二维子数组的和(元素可以连续)
2014-03-29 21:49
411 查看
[b]结对编程训练[/b]
——小组成员:刘铸辉 何晓楠
之前做二维数组连续的二维子数组的和的时候有问题,当时不是很懂,这次跟新问题一起来跟大家讨论下。
[b]扩展问题一:[/b]
[b]二维数组连续的二维子数组的和(矩形)[/b]
首先子数组是个矩形,肯定要先遍历,而且复杂度以很高,如何遍历也是一个问题。
方法1 遍历
这个时候我们可以把每一行看成是一个元素,这样就变成了一个纵向的一维数组了。这样对一维数组的遍历是和以前做的是一样的。而对于每一行我们遍历也是和一维是一样的。编码如下:
方法2 先求部分和
求二维的时候就变成了四个坐标了,不能遍历求和了。可以先求部分和,把他当作已知的,然后定义一个部分和数组PartSum,其中PartSum[i][[j]代表了下标(0,0),(0,j),(i,0),(i,j)包围的区间的和。而此时下标(imin,jmin),(imin,jmax),(imax,jmin),(imax,jmax)包围的区间和就等于
PartSum[imax][[jmax]-PartSum[imin-1][[jmax]-PartSum[imax][[jmin-1]+PartSum[imin-1][[jmin-1]。
这就是要求的PartSum(imin,jmin,imax,jmax),接下来就是求PartSum数组了。而对于每一个PartSum[i][[j]都不是孤立的,都是和其他的有关系的。然后找出这个关系式:
PartSum[i][[j]=PartSum[i-1][[j]+PartSum[i][[j-1]-PartSum[i-1][[j-1]+array[i][j]。
[b]方法3动态规划[/b]
把每一列看成一个元素。这样对于遍历的行区间,我们就可以当成一维来做。对于imin和imax之间的每一列,就相当于一维的一个元素。假设这个一维数组是BC,则BC[j]=array[imin][j]+....+array[imax][j],问题就变成了求BC数组的连续子数组之和的最大值了。而根据刚才求的部分和,我们可以知道对于imin行和imax行之间的区间第j列的值是
BC(PartSum,imin,imax,j)=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1].(此时BC是一个函数)
这样就算是把二维数组连续的二维子数组的和问题解决了,但是这只是求矩形子数组。
[b]扩展问题二:[/b]
[b]二维数组连续的二维子数组的和(左右相连,上下也相连)[/b]
分析思路:这个最大子矩阵和就是把平面上下相连和左右相连,无论先连哪一边都一样。先随便找一条横边和一条竖边即可,然后原问题等价于把4个这样的平面拼在一起,然后找不超过一个平面大小的最大子矩阵。先转换为1维的问题,枚举上下边界,用一维的方法用单调队列求解。编码如下:
有些思路也是从网上借鉴的,看了很多种解法,然后结合自己的解法跟大家分享下。
——小组成员:刘铸辉 何晓楠
之前做二维数组连续的二维子数组的和的时候有问题,当时不是很懂,这次跟新问题一起来跟大家讨论下。
[b]扩展问题一:[/b]
[b]二维数组连续的二维子数组的和(矩形)[/b]
首先子数组是个矩形,肯定要先遍历,而且复杂度以很高,如何遍历也是一个问题。
方法1 遍历
这个时候我们可以把每一行看成是一个元素,这样就变成了一个纵向的一维数组了。这样对一维数组的遍历是和以前做的是一样的。而对于每一行我们遍历也是和一维是一样的。编码如下:
//求二维数组的连续子数组之和的最大值 int MaxSum(int (*array) ) { int i,j; int MaxSum=-INFINITY;//初始化 int imin,imax,jmin,jmax; for(imin=1;imin<=N;imin++) { for(imax=imin;imax<=N;imax++)//当成是遍历纵向的一维 { for(jmin=1;jmin<=M;jmin++) { for(jmax=jmin;jmax<=M;jmax++)//当成是遍历横向的一维 MaxSum=MaxNum(MaxSum,PartSum(imin,jmin,imax,jmax)); } } } return MaxSum; }
方法2 先求部分和
求二维的时候就变成了四个坐标了,不能遍历求和了。可以先求部分和,把他当作已知的,然后定义一个部分和数组PartSum,其中PartSum[i][[j]代表了下标(0,0),(0,j),(i,0),(i,j)包围的区间的和。而此时下标(imin,jmin),(imin,jmax),(imax,jmin),(imax,jmax)包围的区间和就等于
PartSum[imax][[jmax]-PartSum[imin-1][[jmax]-PartSum[imax][[jmin-1]+PartSum[imin-1][[jmin-1]。
这就是要求的PartSum(imin,jmin,imax,jmax),接下来就是求PartSum数组了。而对于每一个PartSum[i][[j]都不是孤立的,都是和其他的有关系的。然后找出这个关系式:
PartSum[i][[j]=PartSum[i-1][[j]+PartSum[i][[j-1]-PartSum[i-1][[j-1]+array[i][j]。
int MaxSum(int (*array) ) { int PartSum[N+1][M+1]; int i,j; for(i=0;i<=N;i++) PartSum[i][0]=0; for(j=0;j<=M;j++) PartSum[0][j]=0; for(i=1;i<=N;i++) for(j=1;j<=M;j++) PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1]; int MaxSum=-INFINITY;//初始化 int imin,imax,jmin,jmax; for(imin=1;imin<=N;imin++) for(imax=imin;imax<=N;imax++) for(jmin=1;jmin<=M;jmin++) for(jmax=jmin;jmax<=M;jmax++) MaxSum=MaxNum(MaxSum,PartSum[imax][jmax]-PartSum[imin-1][jmax]-PartSum[imax][jmin-1]+PartSum[imin-1][jmin-1]); return MaxSum; }
[b]方法3动态规划[/b]
把每一列看成一个元素。这样对于遍历的行区间,我们就可以当成一维来做。对于imin和imax之间的每一列,就相当于一维的一个元素。假设这个一维数组是BC,则BC[j]=array[imin][j]+....+array[imax][j],问题就变成了求BC数组的连续子数组之和的最大值了。而根据刚才求的部分和,我们可以知道对于imin行和imax行之间的区间第j列的值是
BC(PartSum,imin,imax,j)=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1].(此时BC是一个函数)
//求二维数组的连续子数组之和的最大值 int MaxSum(int (*array) ) { int PartSum[N+1][M+1]; int i,j; for(i=0;i<=N;i++) PartSum[i][0]=0; for(j=0;j<=M;j++) PartSum[0][j]=0; for(i=1;i<=N;i++) for(j=1;j<=M;j++) PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1]; int MaxSum=-INFINITY; int Start,All; int imin,imax; for(imin=1;imin<=N;imin++) { for(imax=imin;imax<=N;imax++) { Start=BC(PartSum,imin,imax,M); All=BC(PartSum,imin,imax,M); for(j=M-1;j>=1;j--) { if(Start>0) Start+=BC(PartSum,imin,imax,j); else Start=BC(PartSum,imin,imax,j); if(Start>All) All=Start; } if(All>MaxSum) MaxSum=All; } } return MaxSum; } int BC(int (*PartSum)[N+1],int imin,int imax,int j) //imin--imax第j列的和 { int value; value=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1]; return value; }
这样就算是把二维数组连续的二维子数组的和问题解决了,但是这只是求矩形子数组。
[b]扩展问题二:[/b]
[b]二维数组连续的二维子数组的和(左右相连,上下也相连)[/b]
分析思路:这个最大子矩阵和就是把平面上下相连和左右相连,无论先连哪一边都一样。先随便找一条横边和一条竖边即可,然后原问题等价于把4个这样的平面拼在一起,然后找不超过一个平面大小的最大子矩阵。先转换为1维的问题,枚举上下边界,用一维的方法用单调队列求解。编码如下:
int main() { int m,n; scanf("%d%d",&m,&n); int temp[101][101]; int result[10000]; int s[202][202]; int i,j,p,q; memset(temp,0,sizeof(temp)); memset(s,0,sizeof(s)); memset(result,0,sizeof(result)); //输入二维数组 for( i=1; i<=m; i++) for( j=1; j<=n; j++) { scanf("%d",&s[i][j]); s[i][n+j] = s[i][j]; s[m+i][j] = s[i][j]; s[m+i][n+j] = s[i][j]; } int r =0; for( i=0; i<m;i++) for( j=0; j<n;j++) { for( p=1; p<=m; p++) { for(q=1; q<=n; q++) temp[p][q] = s[p+i][q+j]; } result[r++] = MaxSum2(m,n,temp); //记录各种情况下的最大子矩阵和 } int maxresult = result[0]; //从各种情况下的最大子矩阵和中找出最大的值 for (i=1; i<m;i++) { if(maxresult < result[i]) maxresult = result[i]; } printf("%d\n",maxresult); return 0; } //用动态规划算法计算“最大子段和问题”,对应于一维数组 int MaxSum(int n,int* a) { int sum=0,b=0; for (int i=1;i<=n;i++) { if (b>0) b+=a[i]; else b=a[i]; if (b>sum) sum=b; } return sum; }
int MaxSum2(int m, int n, int a[][101]) { int sum=0; int b[101]; int i,j,k,p,t1=0,t2=0; memset(b,0,sizeof(b)); //内存空间初始化 for ( i=1; i<=m; i++) { int flag_m = m , flag_n = n; for( k=1; k<=2*n; k++) b[k]=0; for ( j=i; j < flag_m + i; j++) { t1 = j>m? j-m : j; for( k=1; k <=n ; k++) { t2 = k>n? k-n : k; b[k] += a[t1][t2]; } int max = MaxSum(2*n,b); if(max>sum) sum = max; } } return sum; }
有些思路也是从网上借鉴的,看了很多种解法,然后结合自己的解法跟大家分享下。
相关文章推荐
- iOS7死机率比安卓高2倍!爱疯5S冲进3K档
- MTU与MSS的关系---以太网帧结构
- css水平垂直居中
- HTTP协议状态码的含义
- ubuntu下字体
- 绪指导的交易
- oracle认证方式
- 线性布局实现垂直分布
- Jquery 资料收集平台
- 单例模式
- 用栈实现计算器
- 学习笔记:第一章 计算机系统基础知识
- Sicily 7693. Cards 解题报告
- maven的一点点的理解
- Lucene 索引库的优化
- 迟来的Hello
- asp.net web.config配置主题
- C语言基础系列--数据进制转换
- HDU yt13双周赛1001 Digital Roots
- asp.net web.config配置主题