编程之美读书笔记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://my.oschina.net/u/3579120/blog/1508387

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值