暑假学的,当时也没总结,今天又看到,怕再忘了吧……
其实就还是dp的思想,搞来搞去这个样子……
思路来源
https://blog.csdn.net/qq_30358129/article/details/88044727
最大01子矩阵
题目可以用来交poj3494
h[i][j]表示以(i,j)为最下位置的悬线的高度,
l[i][j]表示(i,j)能拓到的最左位置,r[i][j]代表(i,j)能拓到的最右位置
枚举每一行,然后如果上一行有值的话就将同行子矩形变窄,否则不变
以上思想即可解决最大01子矩阵问题,然后单调栈也可以解决
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e3+10;
int m,n,a[N][N],l[N][N],r[N][N],h[N][N];
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
}
}
int L=1e9,R=0,ans=0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(a[i][j]==1)L=min(L,j);//尺取连续段最左1的位置
else L=1e9;
l[i][j]=L;
}
for(int j=m;j>=1;--j)
{
if(a[i][j]==1)R=max(R,j);//尺取连续段最右1的位置
else R=0;
r[i][j]=R;
}
for(int j=1;j<=m;++j)
{
if(a[i-1][j])
{
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(a[i][j])ans=max(ans,(r[i][j]-l[i][j]+1)*h[i][j]);
}
}
printf("%d\n",ans);
}
return 0;
}
最大同色子矩阵
魔改一下悬线法,把相同颜色的抠出来,考虑什么时候修改l[]和r[]的值
代码将h[j]不更新时初始化为极小/极大,从而将取max和取min对l[]和r[]的修改统一
相同的思想在于,如果上面可以继承,就一定会缩左界和右界,代表悬线必取的左右界
如果仍用单调栈的话,需要套一个尺取
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e3+10;
typedef long long ll;
int m,n,a[N][N];
int l[N],r[N],h[N];
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
}
}
for(int j=1;j<=m;j++)
l[j]=0,r[j]=m+1,h[j]=0;
ll ans=0;
int la,ra;
for(int i=1;i<=n;++i)
{
la=0;ra=m+1;
for(int j=1;j<=m;++j)
{
if(a[i][j]==a[i-1][j])
h[j]++;
else
h[j]=1,l[j]=0,r[j]=m+1;
if(a[i][j]==a[i][j-1])
l[j]=max(l[j],la+1);
else
l[j]=j,la=j-1;//考虑到l[j]<=j而此处l[j]只能等于j
}
for(int j=m;j>=1;--j)
{
if(a[i][j]==a[i][j+1])
r[j]=min(r[j],ra-1);
else
r[j]=j,ra=j+1;//同理
ans=max(ans,1ll*h[j]*(r[j]-l[j]+1)*a[i][j]);
}
}
printf("%lld\n",ans);
}
return 0;
}