H - Maximal submatrix HDU - 6957

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了H - Maximal submatrix HDU - 6957相关的知识,希望对你有一定的参考价值。

H - Maximal submatrix HDU - 6957

题意:

给定一个n行m列的矩阵,求每列上面积不减的最大子矩阵

对于每个测试用例,打印一个表示最大子矩阵的整数

题解:

要求求一个最大面积的满足每列非递减的矩阵,这怎么想?
我们可以转化成01矩阵,每一个位置1表示该位置比上面一位大,然后求最大的01矩阵就可以了,单调栈做法时注意0的话可以作为矩阵的开始,详细看看代码

代码:

单调栈做法

#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
typedef long long ll;
using namespace std;
//Fe~Jozky
const ll INF=0x3f3f3f3f;
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
int n,m;
const int maxn=2e3+9;
int a[maxn][maxn];
int h[maxn][maxn];
int w[maxn][maxn]; 
int st[maxn],top;
int ans=0;
void precal(){
	memset(w,0,sizeof(w));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(h[i][j]==0)w[i][j]=1;
			else w[i][j]=w[i-1][j]+1;
		}
		w[i][m+1]=-1; 
	}
}
void cal(){
	for(int i=1;i<=n;i++){
		top==0;
		for(int j=1;j<=m+1;j++){
			if(top==0||w[i][j]>=w[i][st[top]]){
				st[++top]=j;
			}
			else 
			{
				int id;
				while(top!=0&&w[i][j]<w[i][st[top]]){
					id=st[top];
					top--;
					ans=max(ans,(j-id)*w[i][id]);
				}
				st[++top]=id;
				w[i][id]=w[i][j];
			}
		}
	}
}
void solve(){
	n=read();m=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			a[i][j]=read();
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]>=a[i-1][j])h[i][j]=1;
			else h[i][j]=0;
		}
	}
	precal();
	cal(); 
	cout<<ans<<endl;
}
int main()
{
	#ifdef ONLINE_JUDGE
    #else
        freopen("1.in","r",stdin);
    #endif
	int t;
	t=read();
	while(t--){
		
		ans=0;
		solve();
	}
	fclose(stdin);
	return 0;
}

纵向的悬线法

#include <iostream>
#include <cstring>
#define FAST ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define endl '\\n'
#define debug(x) cout << #x << " = " << (x) << endl
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)

using namespace std;

const int maxn = 2e3+5;

int n, m;
short a[maxn][maxn];
bool b[maxn][maxn];
short up[maxn][maxn], down[maxn][maxn], len[maxn][maxn];

void tran() {
	mem(b, 0);
	rep(i, 1, n) {
		rep(j, 1, m) {
			if (i == 1) continue;
			else if (a[i][j] >= a[i-1][j]) b[i][j] = 1;
		}
    }
}

void init() {
	rep(i, 1, n) {
		rep(j, 1, m) {
			up[i][j] = i;
			down[i][j] = i;
			len[i][j] = 1;
		}
	}
}

int main()
{
	FAST;
	int t; cin >> t;
	while (t--) {
		cin >> n >> m;
		rep(i, 1, n) {
			rep(j, 1, m) {
				cin >> a[i][j];
			}
		}
		
		tran();
		init();
		
		rep(j, 1, m) {
			rep(i, 2, n) {
				if (b[i][j]) up[i][j] = up[i-1][j];
			}
			
			per(i, n-1, 1) {
				if (b[i+1][j]) down[i][j] = down[i+1][j];
			}
		}
		
		int ans = m;
		
		rep(j, 1, m) {
			rep(i, 1, n) {
				if (b[i][j-1]) {
					len[i][j] = len[i][j-1] + 1;
					up[i][j] = max(up[i][j], up[i][j-1]);
					down[i][j] = min(down[i][j], down[i][j-1]);
				}
				int llen = down[i][j] - up[i][j] + 1;
				ans = max(ans, llen * len[i][j]);
			}
		}
		cout << ans << endl;
	}
 	return 0;
}

另一种悬线法

写法就是:对于每一行的最大范围就是两侧0之间,我们设高度一开始为0,是因为每一个矩阵第一行是可以有0的,但是之后不可以,所以我们最后得到的高度还要算上第一行。相当于我们刨去第一行考虑01矩阵,最后再算上第一行
比如:01矩阵:

01011
01110
11111

我们先不考虑第一行,然后考虑后两行的左右宽度和高,左=2,右为4,高为2,那么答案就是(4-2+1)*(2+1)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=2e3+10;
int t,n,m;
int l[maxn][maxn];
int le[maxn][maxn];
int r[maxn][maxn];
int h[maxn][maxn];
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>m;
		
		memset(le,0,sizeof(le));
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				
				le[0][j]=0;
				scanf("%d",&l[i][j]);
				if(i!=1)
				if(l[i][j]>=l[i-1][j])
				{
					le[i][j]=1;
					
				}
				//else le[i][j]=1;
			}
		}
		memset(l,0,sizeof(l));
		memset(h,0,sizeof(h));
		memset(r,0,sizeof(r));
		int ans=m;
		int L=1e9,R=0;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				if(le[i][j]==1)
				{
					L=min (L,j);
				}
				else L=1e9;
				l[i][j]=L;//如果当前点为0,左端点取正无穷 
			}
			for(int j=m;j>=1;j--)
			{
				if(le[i][j]==1)
				{
					R=max(R,j);
				}
				else R=0;
				r[i][j]=R;//如果当前端点为0,右端点取无穷小 
			}
			for(int j=1;j<=m;j++)
			{
				if(le[i-1][j]==1)
				{
					h[i][j]=h[i-1][j]+1;
					l[i][j]=max(l[i-1][j],l[i][j]);
					r[i][j]=min(r[i-1][j],r[i][j]);
				}
				else h[i][j]=1;
				if(le[i][j]==1)
				{
					ans=max(ans,(r[i][j]-l[i][j]+1)*(h[i][j]+1));
				}
			}
		} 
		cout<<ans<<endl;
	}
	return 0;
}

以上是关于H - Maximal submatrix HDU - 6957的主要内容,如果未能解决你的问题,请参考以下文章

HDOJ6957Maximal submatrix(单调栈,最大子矩阵面积)

hdu 2870 Largest Submatrix

HDU 2870 Largest Submatrix (单调栈)

Leetcode:Maximal Rectangle

B.Maximal Continuous Rest

CF803C Maximal GCD (思维)