CodeVs 1159 最大全0子矩陣【懸線法】
阿新 • • 發佈:2018-12-19
時間限制: 1 s
Description
在一個0,1方陣中找出其中最大的全0子矩陣,所謂最大是指O的個數最多。
Input
輸入檔案第一行為整數N,其中1<=N<=2000,為方陣的大小,緊接著N行每行均有N個0或1,相鄰兩數間嚴格用一個空格隔開。
Output
輸出檔案僅一行包含一個整數表示要求的最大的全零子矩陣中零的個數。
題目分析
表示從向上到第一個1的距離,也即以為底部端點的懸線長度 若,則,否則
再定義表示以為底端點的懸線能向左/右擴充套件的最大距離 向左/右找到第一個,那麼k就是能擴充套件到的左/右端點
最後遍歷更新答案即可
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1; ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=3010;
int n,ans;
int a[maxn][maxn];
int H[maxn][maxn],L[maxn][maxn],R[maxn][maxn];
int main()
{
n=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
a[i][j]=read(),H[i][j]=a[i][j]==0?H[i-1][j]+1:0;;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
int tt=j; L[i][j]=(a[i][j]==0);
while(tt>1&&H[i][tt-1]>=H[i][j])
L[i][j]++,tt--;
}
for(int i=1;i<=n;++i)
for(int j=n;j>=1;--j)
{
int tt=j; R[i][j]=(a[i][j]==0);
while(tt<n&&H[i][tt+1]>=H[i][j])
R[i][j]++,tt++;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
ans=max(ans,H[i][j]*(R[i][j]+L[i][j]-1));
printf("%d",ans);
return 0;
}
到這裡再次發現每次只從上一行轉移 也只從本行的轉移,完全可以滾動陣列優化空間
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=3010;
int n,ans;
int a[maxn];
int H[maxn],L[maxn],R[maxn];
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
a[j]=read(),H[j]=a[j]==0?H[j]+1:0;;
for(int j=1;j<=n;++j)
{
int tt=j; L[j]=(a[j]==0);
while(tt>1&&H[tt-1]>=H[j])
L[j]++,tt--;
}
for(int j=n;j>=1;--j)
{
int tt=j; R[j]=(a[j]==0);
while(tt<n&&H[tt+1]>=H[j])
R[j]++,tt++;
}
for(int j=1;j<=n;++j)
ans=max(ans,H[j]*(R[j]+L[j]-1));
}
printf("%d",ans);
return 0;
}