编程之美读书笔记2.15 - 子数组之和的最大值(二维)

http://blog.csdn.net/pipisorry/article/details/39083073

问题:

求二维数组(矩阵)的子矩阵之和的最大值。

亦可见:http://poj.org/problem?id=1050

解法1:(解释见注释)

每个子矩阵由列长、行长和左上角的元素位置决定。如果我们指定左上角的元素位置 (i,j) 和列长 c,那么可以求所有这些子矩阵中和最大的。然后,变化列长 c,可以求以 (i,j) 为左上角的最大和子矩阵。最所有左上角位置再求最大和子矩阵,问题就解决了。令 OPT(i,j,c) 表示以 (i,j) 为左上角,列长为 c 的最大和子矩阵之和,OPT(i,j) 表示以 (i,j) 为左上角的最优解,而 S(i,u,v) 表示第 i 行中从列 u 到列 v所有元素之和。则

    OPT(i,j,c) = OPT(i+1,j,c) + S(i,j,j+c-1)

    OPT(i,j) = max { OPT(i,j,c) : 1 <= c <= n }

其中,j+c-1 <= n。当 i >m 时, OPT(i,j,c) = 0。一共有 O(mn) 个 OPT(i,j) 子问题,而每个 OPT(i,j) 又可以有 n 个决策,因此,总的解规模有 O(mn2 ) 个 OPT(i,j,c)。每个这样的子问题可以在 O(1) 时间内解决(想想怎么做到),因此,时间复杂度为 O(mn2 )。

/*	直接枚举法O( N^2*M^2*O(sum()) )	Time Limit Exceeded	*/
static int submatrixSum(int **a, int row, int row_end, int col, int col_end){
	int sum = 0;
	for(int i = row; i <= row_end; i++)
		for(int j = col; j <= col_end; j++)
			sum += a[i][j];
	return sum;
}
static int maxSubmatrixSum1(int **a, int n){
	int max_sum = INT_MIN;
	int sum;
	int max_row, max_row_end, max_col, max_col_end;
	for(int row = 0; row < n; row++){
		for(int col = 0; col < n; col++){						//子矩阵左上角位置
			for(int row_end = row; row_end < n; row_end++)
				for(int col_end = col; col_end < n; col_end++){			//4个属性确定一个子矩阵
					sum = submatrixSum(a, row, row_end, col, col_end);	//计算每个子矩阵的和
					if(sum > max_sum){
						max_sum = sum;
						/*max_row = row;
						max_row_end = row_end;
						max_col = col;
						max_col_end = col_end;*/
						//printf("%d\n", max_sum);
					}
				}
		}
	}
	/*printf("\n            col\tcol_end\n            %d\t%d\n", max_col, max_col_end);	//输出子矩阵位置(4个属性)
	printf("row     %d\nrow_end %d\n", max_row, max_row_end);*/
	return max_sum;
}

解法2:

/*	DP算法 O(N^2*M)	*/
static int maxSubmatrixSum2(int **a, int n){
	int ***row_sum = (int ***)malloc(sizeof(int **) * n);
	for(int i = 0; i < n; i++)
		assert( row_sum[i] = (int **)malloc(sizeof(int *) * n) );
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			assert( row_sum[i][j] = (int *)malloc(sizeof(int) * n) );

	//计算row_sum[i][j][c]为第i行j列到c列的和
	for(int i = 0; i < n; i++)								//初始化row_sum[i][j][c]为0
		for(int j = 0; j < n; j++)
			memset(row_sum[i][j], 0, sizeof(int) * n);				//!!!
	for(int i = n - 1; i >= 0; i--){
		for(int j = 0; j < n; j++){
			for(int c = j; c < n; c++)
				if(c == 0)
					row_sum[i][j][c] = a[i][c];
				else
					row_sum[i][j][c] = row_sum[i][j][c - 1] + a[i][c];
		}
	}

	//将row_sum[i][j][c]转换成第i行j列到c列的和的最优解
	for(int i = n - 2; i >= 0; i--){							//row_sum[n-1][j][c]不变
		for(int j = 0; j < n; j++)
			for(int c = j; c < n; c++){
				if(row_sum[i+1][j][c] > 0)
					row_sum[i][j][c] += row_sum[i+1][j][c];
			}
	}

	//求以[i, j]为左上角的矩形最优解
	int **optij = (int **)malloc(sizeof(int *) * n);
	for(int i = 0; i < n; i++)
		optij[i] = (int *)malloc(sizeof(int) * n);
	for(int i = 0; i < n; i++)
		memset(optij[i], INT_MIN, sizeof(int) * n);
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++)
			for(int c = j; c < n; c++){
				if(row_sum[i][j][c] > optij[j][j])
					optij[j][j] = row_sum[i][j][c];
			}
	}

	//求整体最优解
	int max_sum = INT_MIN;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			if(optij[i][j] > max_sum)
				max_sum = optij[i][j];
		}
	}

	return max_sum;
}

解法3:

/*	最优算法	AC 63MS */
static int maxSubmatrixSum(int **a, int n){
	//初始化col_sum, col_sum[i][j][k]为第i和j行之间第k列元素的和
	int *** col_sum;
	assert( col_sum = (int ***)malloc(sizeof(int **) * n) );
	for(int i = 0; i < n; i++)
		assert( col_sum[i] = (int **)malloc(sizeof(int *) * n) );
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			assert( col_sum[i][j] = (int *)malloc(sizeof(int) * n) );
			memset(col_sum[i][j], 0, sizeof(int) * n);
		}
	}

	//计算第0和j(>=1)行之间第k列的和
	for(int k = 0; k < n; k++){	
		col_sum[0][0][k] = a[0][k];					//初始化col_sum[0][0][k]为首行数据
		for(int j = 1; j < n; j++)
			col_sum[0][j][k] = col_sum[0][j - 1][k] + a[j][k];	//!!!
	}
	//计算第i和j行之间第k列的和
	for(int k = 0; k < n; k++){
		for(int i = 1; i < n; i++)
			for(int j = i; j < n; j++)
				col_sum[i][j][k] = col_sum[i - 1][j][k] - a[i - 1][k];
	}

	//计算最大子矩阵和
	int max_mat_sum = INT_MIN;
	for(int i = 0; i < n; i++){
		for(int j = i; j < n; j++){
			int row_sum = 0;					//第i和j行之间最大行array和(压缩矩阵)
			for(int k = 0; k < n; k++){
				row_sum += col_sum[i][j][k];
				if(row_sum < 0)
					row_sum = 0;
				if(row_sum > max_mat_sum)
					max_mat_sum = row_sum;
			}
		}
	}

	return max_mat_sum;
}

测试:

int main(){
	assert( freopen("BOP\\maxSubmatrixSum.in", "r", stdin) );
	//int cases;									//测试案例数目
	//scanf("%d", &cases);
	//while(cases--){
		int n;									//每个案例中matrix维度
		scanf("%d", &n);
		int **a = (int **)malloc(sizeof(int*) * n);
		for(int i = 0; i < n; i++)
			a[i] = (int *)malloc(sizeof(int) * n);


		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++)
				scanf("%d", &a[i][j]);


		//printf("%d\n", maxSubmatrixSum1(a, n) );
		//printf("%d\n", maxSubmatrixSum2(a, n) );
                printf("%d\n", maxSubmatrixSum(a, n) );
	//}
	fclose(stdin);
	return 0;
}

测试案例:

3

2
1 1
1 1

4
0 -2 -7 0 
9 2 -6 2 
-4 1 -4 1 
-1 8 0 -2 

3
3 -1 4
3 -1 4
3 -1 4

output:
4
15
18

from:

http://blog.csdn.net/pipisorry/article/details/39083073

ref:

Maximum Submatrix & Largest Rectangle

编程之美 书p190

    原文作者:-柚子皮-
    原文地址: https://blog.csdn.net/pipisorry/article/details/39083073
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞