题意:给你一个01矩阵,求其中第二大的子矩阵是多少
开始想用DP 但维护的只是以每个点为矩阵的右下点对应的最大矩阵,最后通过率才92%。。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e3+3;
int map[maxn][maxn]={0};
struct node{
int lx,ly,rx,ry;
int size;
node(int _lx,int _ly,int _rx,int _ry):lx(_lx),ly(_ly),rx(_rx),ry(_ry)
{
size=(rx-lx+1)*(ry-ly+1);
}
node()
{
}
bool operator<(const node & tmp)const{
return size<tmp.size;}
};
node dp[maxn][maxn];
int mp_col[maxn][maxn]={0};
int mp_row[maxn][maxn]={0};
int main()
{
int n,m;
cin>>n>>m;int maxsize=0;
int second_maxsize=0;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=m;i++)
{
scanf("%1d",&map[j][i]);
}
}
for(int j=1;j<=n;j++)
{
for(int i=1;i<=m;i++)
{
if(map[j][i]==1)
mp_row[j][i]=mp_row[j][i-1]+1;
}
}
for(int j=1;j<=m;j++)
{
for(int i=1;i<=n;i++)
{
if(map[i][j]==1)
mp_col[i][j]=mp_col[i-1][j]+1;
}
}
node tmp1,tmp2;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=m;i++)
{
if(map[j][i]==1)
{
tmp1=node(i,j,i,j);
tmp2=node(i,j,i,j);
if(map[j-1][i]!=0)//这里是核心的状态转移方程
if(mp_row[j][i]>=dp[j-1][i].rx-dp[j-1][i].lx+1)
tmp1=node(dp[j-1][i].lx,dp[j-1][i].ly,i,j);
else
tmp1=node(i+1-mp_row[j][i],dp[j-1][i].ly,i,j);
else
tmp1=node(i+1-mp_row[j][i],j,i,j);
if(map[j][i-1]!=0)
if(mp_col[j][i]>=dp[j][i-1].ry-dp[j][i-1].ly+1)
tmp2=node(dp[j][i-1].lx,dp[j][i-1].ly,i,j);
else
tmp2=node(dp[j][i-1].lx,j+1-mp_col[j][i],i,j);
else
tmp2=node(i,j+1-mp_col[j][i],i,j);
dp[j][i]=max(tmp1,tmp2);
maxsize=max(dp[j][i].size,maxsize);
}
}
}
bool flag=0;int cnt=0;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=m;i++)
{
if(dp[j][i].size==maxsize)
{
cnt++;
}
else
{
second_maxsize=max(second_maxsize,dp[j][i].size);
}
}
}
if(cnt>1)
second_maxsize=maxsize;
cout<<second_maxsize<<endl;
return 0;
}
但是这个其实求最大矩阵有用,但是中间会舍弃很多不是最大的情况,所以求的答案不对
最后实在没办法用了单调栈
单调栈的核心优势,其在入栈出栈的过程,能线性复杂度的找到数列中每个元素之后或之前第一个比它大或小的元素。因此在这种统计矩阵等问题有很大应用优势。
细节:更新答案的时候,不光计算x*y,还有(x-1)*y和(y-1)*x的情况。因为加入题目问的是最大的矩阵,单调栈统计的只是以每行为底所有的最大的矩阵,但是万一最后统计的只有一个矩阵或者其他类似情况,第二大的矩阵很可能是其包含的子矩阵,而这个子矩阵应该是面积刚好比母矩阵小一点点,故其面积只可能是(x-1)*y和(y-1)*x。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn= 1e3+100;
int s[maxn][maxn];
int st[maxn];
int f_max=0,s_max=0;
void update(int tmp)
{
if(tmp>=f_max)//我觉得这里还是得>= 不过有个题解是>也过了。。。
{
s_max=f_max;
f_max=tmp;
}
else
{
s_max=max(s_max,tmp);
}
return;
}
void calcu(int x,int y)
{
update(x*y);
update(x*(y-1));
update((x-1)*y);
return;
}
int main()
{
int n,m;
cin>>n>>m;int tmp;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%1d",&tmp);
if(tmp==1)
{
s[i][j]=s[i-1][j]+1; //算每个点对应的向上有多少1
}
}
}
for(int i=1;i<=n;i++)
{
int top=0;
s[i][m+1]=-1;
st[top]=0;
s[i][0]=-2;
for(int j=1;j<=m+1;j++)
{
while(s[i][st[top]]>s[i][j]) //核心单调栈
{
int index=st[top];
top--;
calcu(j-st[top]-1,s[i][index]);
}
st[++top]=j;
}
}
cout<<s_max<<endl;
}
第八场H题:
题意:
给一个01矩阵,求其中极大子矩阵的数量,极大子矩阵即其不会被其他的子矩阵完全包含。
参考blog:
https://blog.csdn.net/qq_43813163/article/details/99111888
单调栈的应用优势即在这种题目中统计到每行中以每个格子的向上高度为高的最大矩阵。我重新思考了,感觉上面的代码有问题,但就是过了,又找了一篇这个才对,个人认为https://blog.csdn.net/lgz0921/article/details/96700527
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn= 3e3+100;
int s[maxn][maxn];
int l[maxn][maxn];//每个点左侧(包括自己)1的个数
int f_max=0,s_max=0;
int ans=0;
stack< pair<int,int> > st;
void calcu(int x,int y1,int y2)
{
if(y2-y1+1<=l[x+1][y2])
return;
ans++;
// cout<<x<<" "<<y1<<" "<<y2<<endl;
}
int main() {
int n,m;
cin>>n>>m;int tmp;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%1d",&tmp);
if(tmp==1)
{
s[i][j]=s[i-1][j]+1; //算每个点对应的向上有多少1
l[i][j]=l[i][j-1]+1;
}
}
}
for(int i=1;i<=n;i++)
{
while(!st.empty()) st.pop();
for(int j=1;j<=m+1;j++)
{
int pos=j;
while((!st.empty())&&st.top().first>s[i][j]) //核心单调栈
{
pos=st.top().second;
calcu(i,st.top().second,j-1);
st.pop();
}
if(s[i][j]&&(st.empty()||st.top().first<s[i][j]))//注意 不要把两个相邻的且同样高度的加进栈里,否者会重复计算
st.push({s[i][j],pos});
}
}
cout<<ans<<endl;
}