直方图中最大矩形面积(单调栈) + 最大的全1子矩阵(HDU1505) + 两个矩阵最大的相同子矩阵 + 最大子矩阵和

直方图中最大矩形面积

  • 思想:主要是采用单调栈的思想,栈中按照每个矩形的高度递增存储下标,每次需要出栈时计算面积大小。
  • 算法过程:
    给定的n个值组成的序列,每个值代表矩形高度,宽度都相等。
    在这里插入图片描述
    1.初始栈空,第一个下标直接入栈;
    2.遍历到 i 时,比较与栈顶元素对应的大小
    若等于栈顶元素,则跳过;
    若大于栈顶元素,则入栈;
    若小于栈顶元素,则一直取出栈顶元素t,并计算面积( ( i - t ) * a[t] ),直到栈顶元素小于a[i],入栈。
    3.最后清空栈内元素,计算面积( n - t + 1 ) * a[t] ;
    途中统计最大值即可。

  • 利用这种思想,可以求最大全1子矩阵
    • 从第一行开始向下遍历,对于每一行构造直方图,数组a存放高度;
    • 若G[i][j]为1,则a[j] ++ ;
    • 若G[i][j]为0,则a[j] = 0 ;
    • 对每一行得出的直方图,进行直方图中最大矩形面积 算法,计算最大面积即为最大的全1子矩阵。

例题:hdu1505

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e3 + 66 ;
char G[AX][AX]; 
int a[AX] ; 
int main(){
	int T , m , n ; 
	scanf("%d",&T);
	while( T-- ){
		int res = 0 ; 
		scanf("%d%d",&m,&n);
		for( int i = 1 ; i <= m ; i++ ){
			for( int j = 1 ; j <= n ; j++ ){
				scanf(" %c",&G[i][j]);
			}
		}
		memset( a , 0 , sizeof(a) );
		for( int i = 1 ; i <= m ; i++ ){
			for( int j = 1 ; j <= n ; j++ ){
				if( G[i][j] == 'F' ) a[j] ++ ; 
				else a[j] = 0 ; 
			}
			//******************直方图最大矩形面积********************
			stack<int>s ;
			for( int k = 1 ; k <= n ; k++ ){
				if( !s.size() ){  //栈空,直接入栈
					s.push(k);
				}else{
					int t = s.top();
					if( a[t] == a[k] ) continue ; //与栈顶元素相等,跳过
					if( a[t] < a[k] ){ //大于栈顶元素,直接入栈
						s.push(k) ;
					}else{
						while( a[t] > a[k] ){ //弹出栈中大于当前值的元素并计算面积
							res = max( res , ( k - 1 - t + 1 ) * a[t] ) ;
							s.pop();
							if( !s.size() ) break ;
							t = s.top();
						}
						s.push(k);
					}
				}
			}
			while( s.size() ){//清空栈,右边界相当于宽度的右端点
				int t = s.top() ; 
				res = max( res , ( n - t + 1 ) * a[t] );
				s.pop();
			}
			//****************************************************
		}
		printf("%d\n",res*3);
	}
	return 0 ; 
}

多个矩阵最大的相同子矩阵(方阵)。

思路(2个为例):只需要统计每个矩阵的每个位置是否相同,相同则为1,不同则为0;

1 1 1 1
1 1 1 0
1 1 1 0
1 1 1 1

0 1 1 1
0 1 1 1
0 1 1 1
0 1 1 0
统计后变为
0 1 1 1
0 1 1 0
0 1 1 0
0 1 1 0

然后按照求最大的全1子矩阵做即可。(方阵只需在统计结果时判断高和宽是否相等,相等则统计,不等不用统计)


最大子矩阵和

基本思路是转化成数组的最大连续和
mp[i][j] : 表示1-i行第j列之和:遍历起止行,对于每个起止行求数组的最大连续和

时间O(n^3)空间O( n ^ 2 )

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e2 + 66 ;
int mp[AX][AX] ;  //mp[i][j] : 1 - i 行的第j列之和
int main() {
	int n ;
	while( ~scanf("%d",&n) ) {
		int res = -INF ;
		memset( mp , 0 , sizeof(mp) ) ;
		for( int i = 1 ; i <= n ; i++ ) {
			for( int j = 1 ; j <= n ; j++ ) {
				scanf("%d",&mp[i][j]);
				mp[i][j] += mp[i-1][j] ;
			}
		}
		for( int i = 1 ; i <= n ; i++ ) {
			for( int j = i ; j <= n ; j++ ) {
				int sum = 0 ;
				for( int k = 1 ; k <= n ; k++ ) {
					if( sum + mp[j][k] - mp[i-1][k] >= 0 ) {
						sum += mp[j][k] - mp[i-1][k] ;
					} else sum = 0 ;
					res = max( res , sum );
				}
			}
		}

		printf("%d\n",res);
	}
	return 0 ;
}

下面是一种更为简单的矩阵和,即给定了矩阵大小要求,直接存储dp[i][j]为**(1,1)-(i,j)的和**,然后暴力矩阵右下角点即可。
dp[i][j]存储1-i行,1-j列的矩阵和;
dp[i][j] =a[i][j] + dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] ;

例题:hdu1559

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e3 + 66 ;
int dp[AX][AX] ;  
int main(){
	int T , m , n , x , y ;
	scanf("%d",&T);
	while( T-- ){
		memset( dp , 0 , sizeof(dp) );
		scanf("%d%d%d%d",&m,&n,&x,&y);
		int res = 0 ; 
		for( int i = 1 ; i <= m ; i++ ){
			for( int j = 1 ; j <= n ; j++ ){
				scanf("%d",&dp[i][j]);
				dp[i][j] += ( dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] ) ; 
				if( i >= x && j >= y ) res = max( res , dp[i][j] - dp[i-x][j] - dp[i][j-y] + dp[i-x][j-y] );
			}
		}
		printf("%d\n",res);
	}
	return 0 ;
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/104673432
今日推荐