您的位置:首页 > 其它

最大子矩阵问题

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个整数;
输出 输出矩阵的最大子矩阵的元素之和。 样例输入
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)的子矩阵,其各元素之和记为:


(1)
最大子矩阵和问题的最优解即为:



(2)



(3)
如果令:




那么
(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
矩阵a的行m=3,列数n=3,矩阵的元素分布如上图所示。
当i = 1时,初始化数组b,使得
b:
0
0
0
当j = 1时,k从1递增到n,由算法的第8行可得数组b将首先存储矩阵的第一行的各值,即b为:
b:
-2
10
20
由最大子序列和的函数Max_Sub_List返回该序列的最大子序列的和值为max=30;
当j = 2时,k 从1递增到n,由算法的第8行可得数组b将矩阵a第二行的值分别加到原有各值上,可得数组b为
b:
98
9
18
同理,由函数Max_Sub_List返回该序列的最大子序列的和值为max=125。
当j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
b:
98
7
15
同理,由函数Max_Sub_List返回该序列的最大子序列的和值为max=120。
到此,i的第一次循环结束。
当i = 2时,从新初始化数组b,使得
b:
0
0
0
j = 2时,k从1递增到n,由算法的第8行可得数组b将首先存储矩阵的第二行的各值,即b为:
b:
100
-1
-2
由函数Max_Sub_List返回该序列的最大子序列的和值为max=100。
j = 3时,k从1递增到n,由算法的第8行可得数组b将矩阵a第三行的值分别加到原有各值上,可得数组b为
b:
100
-3
-5
由函数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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: