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#elsefreopen("1.in","r",stdin);#endifint 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;
}