最大子矩阵问题
2012-12-05 16:39
344 查看
最大和 时间限制:1000 ms | 内存限制:65535 KB 难度:5 描述 给定一个由整数组成二维矩阵(r*c),现在需要找出它的一个子矩阵,使得这个子矩阵内的所有元素之和最大,并把这个子矩阵称为最大子矩阵。
例子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩阵为:
9 2
-4 1
-1 8
其元素总和为15。
输入 第一行输入一个整数n(0<n<=100),表示有n组测试数据;
每组测试数据:
第一行有两个的整数r,c(0<r,c<=100),r、c分别代表矩阵的行和列;
随后有r行,每行有c个整数;
输出 输出矩阵的最大子矩阵的元素之和。 样例输入
问题描述:给定一个m行n 列的整数矩阵a,试求矩阵a 的一个子矩阵,使其各元素之和为最大。
分析:用2 维数组a[1 : m][1 : n]表示给定的m行n列的整数矩阵。子数组a[i1 : i2][j1 : j2]表示左上角和右下角行列坐标分别为(i1, j1)和(i2, j2)的子矩阵,其各元素之和记为:
![](http://img8.ph.126.net/_NOdcca9vVOm7hKie21a4g==/2522297266321873378.jpg)
(1)
最大子矩阵和问题的最优解即为:
![](http://img1.ph.126.net/a0soB916y1D_0FLrcVNeNw==/1087056360074047831.jpg)
(2)
![](http://img4.ph.126.net/sGdmgJtCg5tUCVjpD36L7w==/1031605789662227164.jpg)
(3)
如果令:
![](http://img6.ph.126.net/5ciubMKi2IcEIPGbDerGnA==/188025284460212776.jpg)
![](http://img1.ph.126.net/UlpjmkQK3oe87SERWPDl0g==/44473046337760171.jpg)
那么
(4)
式(4)就是我们熟悉的最大子序列和的问题。
根据以上分析我们可得到最大子矩阵和问题的算法:
Max_Sub_Matrix(m, n, a) //m为行数,n为列数,a为二维矩阵
sum ← 0
b
for i←1 to m
for t←1 to n
do b[t] = 0 //初始化数组b
for j←i to m
do for k←1 to n
b[k] ← b[k] + a[j][k]
max←Max_Sub_List(n, b) //函数返回n个数的序列b的最大子序列的和
if max > sum then sum = max
return sum
下面我们就通过来同学上课提出的一些特殊情况来检验算法的正确性。
矩阵a的行m=3,列数n=3,矩阵的元素分布如上图所示。
当i = 1时,初始化数组b,使得
当j = 1时,k从1递增到n,由算法的第8行可得数组b将首先存储矩阵的第一行的各值,即b为:
由最大子序列和的函数Max_Sub_List返回该序列的最大子序列的和值为max=30;
当j = 2时,k 从1递增到n,由算法的第8行可得数组b将矩阵a第二行的值分别加到原有各值上,可得数组b为
同理,由函数Max_Sub_List返回该序列的最大子序列的和值为max=125。
当j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
同理,由函数Max_Sub_List返回该序列的最大子序列的和值为max=120。
到此,i的第一次循环结束。
当i = 2时,从新初始化数组b,使得
j = 2时,k从1递增到n,由算法的第8行可得数组b将首先存储矩阵的第二行的各值,即b为:
由函数Max_Sub_List返回该序列的最大子序列的和值为max=100。
j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
由函数Max_Sub_List返回该序列的最大子序列的和值为max=92。
到此,i的第二次循环结束。
当i=3循环结束时,可求出该矩阵的最大子矩阵和值为125。
算法的第7到第9行求出每行i固定,j变化的最大子序列和的最大值,第6行为可能与第i行一起构成子矩阵的行,将其值对应加到第i行,然后求该子序列的最大值。而最外层的循环为分别求出每一行的子序列最大值。
说明:最大子序列的解法大家可根据上课介绍的方法使用动态规划法来构造。而大家在网上看到的解法一般不能处理(-1,-2,-3),类似于这种所有元素均为负值的序列,而且也不能够方便的处理返回最小个数的位置。
进一步实践:
1、大家可在上面提供的算法基础上扩充使得该算法能返回最小矩阵的位置;
2、如何解3维的情况(解法与2维类似,转化为一维的方式处理。
思路:我们知道求数组的最大子串和,推广到矩阵的最大子矩阵和。可以基于下面的等式:
S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + E[i][j].其中S[i][j] 表示矩阵A[i][j]所有元素的和,E[i][j]表示第i行j列的元素大小。通过这个递推公司可以得到O(n^4)复杂度的算法。这里我介绍另外一种算法,其复杂度是O(n^3)的。思路如下:先从第一行开始得到以第一行元素为首行的子矩阵的最大和,然后是以第二行为首行的子矩阵的最大和,以此类推,得到最后的最大和。算法实现如下:
/*求数据A的最大子串和*/
int Util::maxsubarray(int* A, int n)
{
int maxsum = 0;
int cursum = 0;
for (int i = 0; i < n ; i++)
{
if (cursum <= maxsum)
cursum += A[i];
else
maxsum = cursum;
if (cursum < 0)
cursum = 0;
}
return maxsum;
}
/*求矩阵A的最大子矩阵和*/
int Util::maxsubmatrix(int** A, int n, int m)
{
int * B = (int *)calloc(m, sizeof(int));
int maxsum = 0;
int cursum = 0;
for (int i = 0; i < n; i ++)
{
memset(B, 0, m);
for ( int j = i; j < n; j ++)
{
for (int l = 0; l < m; l++)
B[l] += A[j][l];
cursum = maxsubarray(B, m);
if (cursum > maxsum)
maxsum = cursum;
}
}
free[] B;
return maxsum;
}
程序代码为
#include<stdio.h>
#include<string.h>
int a[101][101]={0},b[101]={0};
int maxlist(int *a,int n)
{
int i,max=-0x7FFFFFFF,sum=0,max1=-0x7FFFFFFF;
for(i=1;i<=n;i++)
{
if(a[i]>max1) max1=a[i];
sum+=a[i];
if(sum<0) sum=0;
else if(sum>max) max=sum;
}
return (max1<0?max1:max);
}
int main()
{
int n,m,max,sum,i,j,k,N;
scanf("%d",&N);
while(N--)
{
scanf("%d %d",&m,&n);//m行n列
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(i=1,sum=0,max=-0x7FFFFFFF;i<=m;i++)
{
memset(b,0,sizeof(b));
for(j=i;j<=m;j++)
{
for(k=1;k<=n;k++)
{
b[k]+=a[j][k];
//printf("%d ",b[k]);
}
//printf("\n");
sum=maxlist(b,n);
//printf("%d\n",sum);
if(sum>max) max=sum;
}
}
printf("%d\n",max);
}
//system("pause");
return 0;
}
例子:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩阵为:
9 2
-4 1
-1 8
其元素总和为15。
输入 第一行输入一个整数n(0<n<=100),表示有n组测试数据;
每组测试数据:
第一行有两个的整数r,c(0<r,c<=100),r、c分别代表矩阵的行和列;
随后有r行,每行有c个整数;
输出 输出矩阵的最大子矩阵的元素之和。 样例输入
1 4 4 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2样例输出
15最大子矩阵和问题
问题描述:给定一个m行n 列的整数矩阵a,试求矩阵a 的一个子矩阵,使其各元素之和为最大。
分析:用2 维数组a[1 : m][1 : n]表示给定的m行n列的整数矩阵。子数组a[i1 : i2][j1 : j2]表示左上角和右下角行列坐标分别为(i1, j1)和(i2, j2)的子矩阵,其各元素之和记为:
![](http://img8.ph.126.net/_NOdcca9vVOm7hKie21a4g==/2522297266321873378.jpg)
(1)
最大子矩阵和问题的最优解即为:
![](http://img1.ph.126.net/a0soB916y1D_0FLrcVNeNw==/1087056360074047831.jpg)
(2)
![](http://img4.ph.126.net/sGdmgJtCg5tUCVjpD36L7w==/1031605789662227164.jpg)
(3)
如果令:
![](http://img6.ph.126.net/5ciubMKi2IcEIPGbDerGnA==/188025284460212776.jpg)
![](http://img1.ph.126.net/UlpjmkQK3oe87SERWPDl0g==/44473046337760171.jpg)
那么
(4)
式(4)就是我们熟悉的最大子序列和的问题。
根据以上分析我们可得到最大子矩阵和问题的算法:
Max_Sub_Matrix(m, n, a) //m为行数,n为列数,a为二维矩阵
sum ← 0
b
for i←1 to m
for t←1 to n
do b[t] = 0 //初始化数组b
for j←i to m
do for k←1 to n
b[k] ← b[k] + a[j][k]
max←Max_Sub_List(n, b) //函数返回n个数的序列b的最大子序列的和
if max > sum then sum = max
return sum
下面我们就通过来同学上课提出的一些特殊情况来检验算法的正确性。
1 | 2 | 3 | |
1 | -2 | 10 | 20 |
2 | 100 | -1 | -2 |
3 | 0 | -2 | -3 |
当i = 1时,初始化数组b,使得
b: | 0 | 0 | 0 |
b: | -2 | 10 | 20 |
当j = 2时,k 从1递增到n,由算法的第8行可得数组b将矩阵a第二行的值分别加到原有各值上,可得数组b为
b: | 98 | 9 | 18 |
当j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
b: | 98 | 7 | 15 |
到此,i的第一次循环结束。
当i = 2时,从新初始化数组b,使得
b: | 0 | 0 | 0 |
b: | 100 | -1 | -2 |
j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
b: | 100 | -3 | -5 |
到此,i的第二次循环结束。
当i=3循环结束时,可求出该矩阵的最大子矩阵和值为125。
算法的第7到第9行求出每行i固定,j变化的最大子序列和的最大值,第6行为可能与第i行一起构成子矩阵的行,将其值对应加到第i行,然后求该子序列的最大值。而最外层的循环为分别求出每一行的子序列最大值。
说明:最大子序列的解法大家可根据上课介绍的方法使用动态规划法来构造。而大家在网上看到的解法一般不能处理(-1,-2,-3),类似于这种所有元素均为负值的序列,而且也不能够方便的处理返回最小个数的位置。
进一步实践:
1、大家可在上面提供的算法基础上扩充使得该算法能返回最小矩阵的位置;
2、如何解3维的情况(解法与2维类似,转化为一维的方式处理。
思路:我们知道求数组的最大子串和,推广到矩阵的最大子矩阵和。可以基于下面的等式:
S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + E[i][j].其中S[i][j] 表示矩阵A[i][j]所有元素的和,E[i][j]表示第i行j列的元素大小。通过这个递推公司可以得到O(n^4)复杂度的算法。这里我介绍另外一种算法,其复杂度是O(n^3)的。思路如下:先从第一行开始得到以第一行元素为首行的子矩阵的最大和,然后是以第二行为首行的子矩阵的最大和,以此类推,得到最后的最大和。算法实现如下:
/*求数据A的最大子串和*/
int Util::maxsubarray(int* A, int n)
{
int maxsum = 0;
int cursum = 0;
for (int i = 0; i < n ; i++)
{
if (cursum <= maxsum)
cursum += A[i];
else
maxsum = cursum;
if (cursum < 0)
cursum = 0;
}
return maxsum;
}
/*求矩阵A的最大子矩阵和*/
int Util::maxsubmatrix(int** A, int n, int m)
{
int * B = (int *)calloc(m, sizeof(int));
int maxsum = 0;
int cursum = 0;
for (int i = 0; i < n; i ++)
{
memset(B, 0, m);
for ( int j = i; j < n; j ++)
{
for (int l = 0; l < m; l++)
B[l] += A[j][l];
cursum = maxsubarray(B, m);
if (cursum > maxsum)
maxsum = cursum;
}
}
free[] B;
return maxsum;
}
程序代码为
#include<stdio.h>
#include<string.h>
int a[101][101]={0},b[101]={0};
int maxlist(int *a,int n)
{
int i,max=-0x7FFFFFFF,sum=0,max1=-0x7FFFFFFF;
for(i=1;i<=n;i++)
{
if(a[i]>max1) max1=a[i];
sum+=a[i];
if(sum<0) sum=0;
else if(sum>max) max=sum;
}
return (max1<0?max1:max);
}
int main()
{
int n,m,max,sum,i,j,k,N;
scanf("%d",&N);
while(N--)
{
scanf("%d %d",&m,&n);//m行n列
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(i=1,sum=0,max=-0x7FFFFFFF;i<=m;i++)
{
memset(b,0,sizeof(b));
for(j=i;j<=m;j++)
{
for(k=1;k<=n;k++)
{
b[k]+=a[j][k];
//printf("%d ",b[k]);
}
//printf("\n");
sum=maxlist(b,n);
//printf("%d\n",sum);
if(sum>max) max=sum;
}
}
printf("%d\n",max);
}
//system("pause");
return 0;
}
相关文章推荐
- 最大子矩阵问题hdu 1081
- 动态规划(DP)——HDU1081、PKU1050 To The Max 最大子矩阵问题
- pku poj 1050 最大子矩阵问题
- POJ1050与最大连续子段和,最大子矩阵问题
- 子矩阵的最大累加和问题
- 最大子矩阵问题
- Dp_关于最大子矩阵的问题总结
- 最大子矩阵问题<DP>
- 最大子矩阵问题(动态规划的推广)
- 动态规划1:最大子段和问题到最大子矩阵问题(一):最大子段和问题详谈
- 最大子矩阵问题-hdu-1559
- 最大子矩阵求和问题
- 最大子矩阵问题
- ACM——最大子矩阵问题——单位矩阵
- 动态规划解决最大子矩阵问题
- 数据结构与算法——求最大子矩阵问题
- 经典问题之最大子矩阵
- UVa 10755 - Garbage Heap 三维最大子矩阵问题转化为1维..
- 结对开发——求环形二维数组最大子矩阵和的问题
- 最大子矩阵问题实例解析