昨天晚上刷水题写到一道最大全1正方形,于是兴致勃勃将系列相关都复习了一遍qwq
1.最大子段和
测试地址:https://www.luogu.org/problem/show?pid=1115
Problem:给出一段序列,选出其中连续且非空的一段使得这段和最大。
Key:f[i]表示以i为终点时的最大子段和,f[i]=max(f[i-1],0)+a[i];ans=max(ans,f[i]);
1 #include<cmath> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 const int maxn=200009; 7 int a[maxn],f[maxn]; 8 int main() 9 { 10 int n; 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++) 13 { 14 scanf("%d",&a[i]); 15 } 16 for(int i=1;i<=n;i++) 17 { 18 f[i]=max(f[i-1],0)+a[i]; 19 } 20 int ans=-0x3f3f3f3f; 21 for(int i=1;i<=n;i++) 22 { 23 ans=max(ans,f[i]); 24 } 25 cout<<ans<<endl; 26 return 0; 27 }
2.最大子矩形和(最大加权矩形)
测试地址:https://www.luogu.org/problem/show?pid=1719
Problem:给定一个整数矩阵,求其中一个任意大小的子矩形,使该子矩形内值的和最大。
Key:将二维的矩阵按行压成一维,枚举列来做最大子段和,复杂度O(n^3)。
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 const int maxn=150; 8 int a[maxn][maxn],n,m,sum[maxn]; 9 int ans=-0x3f3f3f3f; 10 int f[maxn]; 11 int calc_max(int val[],int n) 12 { 13 int ret=-0x3f3f3f3f; 14 f[1]=val[1]; 15 for(int i=2;i<=n;i++) 16 f[i]=max(f[i-1],0)+val[i],ret=max(ret,f[i]); 17 return ret; 18 } 19 20 int main() 21 { 22 scanf("%d",&n); 23 m=n; 24 for(int i=1;i<=n;i++) 25 { 26 for(int j=1;j<=m;j++) 27 { 28 scanf("%d",&a[i][j]); 29 } 30 } 31 for(int i=1;i<=n;i++) 32 { 33 for(int k=1;k<=m;k++) 34 { 35 sum[k]=0; 36 } 37 for(int j=i;j<=n;j++) 38 { 39 int k; 40 for(k=1;k<=m;k++) 41 { 42 sum[k]+=a[j][k]; 43 } 44 int mx=calc_max(sum,k); 45 ans=max(ans,mx); 46 } 47 } 48 cout<<ans<<endl; 49 return 0; 50 }
3.最大全1正方形
测试地址:https://www.luogu.org/problem/show?pid=1387
Problem:在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长。
Key:
解法1:暴力维护二维前缀和,枚举边长来更新ans,其中求二维前缀和是容斥原理。
1 #include<iostream> 2 #include<cmath> 3 #include<cstring> 4 #include<cstdio> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=109; 8 int n,m,sum[maxn][maxn],ans=0; 9 int main() 10 { 11 scanf("%d%d",&n,&m); 12 for(int i=1;i<=n;i++) 13 { 14 for(int j=1;j<=m;j++) 15 { 16 int x; 17 scanf("%d",&x); 18 sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x; 19 } 20 } 21 for(int i=1;i<=n;i++) 22 { 23 for(int j=1;j<=m;j++) 24 { 25 for(int l=1;l+i<=n&&l+j<=m;l++) 26 { 27 int ok=sum[i+l][j+l]-sum[i+l][j]-sum[i][j+l]+sum[i][j]; 28 if(ok==l*l) 29 { 30 ans=max(ans,l); 31 } 32 } 33 } 34 } 35 printf("%d",ans); 36 return 0; 37 }
解法2:f[i][j]表示以(i,j)为右下角的时候,能延伸左上角到最大的的正方形。如果(i,j)这个点为0时直接不考虑,不为0时才计算转移方程f[i,j]=min(f[i-1,j-1],f[i-1,j],f[i,j-1]),转移方程的实际意义是右上方、左方、右方能扩展的最大边长+1 (加1的操作就是计算(i,j)本身)。
1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 const int maxn=109; 8 int f[maxn][maxn],n,m,ans=0; 9 int main() 10 { 11 scanf("%d%d",&n,&m); 12 for(int i=1; i<=n; i++) 13 for(int j=1; j<=m; j++) 14 { 15 int x; 16 scanf("%d",&x); 17 if(x)//如果这一个格子是1 18 {//f[i][j]表示以(i,j)为右下角的,能延伸左上角最大的(正方形)边长 19 f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1; 20 //取min是为了验证是不是三个格子都能达到扩展要求 21 //如果其中一个值为0,说明那个角没法扩展正方形 22 //如果其中最小值为x,说明这个点最多扩展x,加他自己就是+1 23 ans=max(ans,f[i][j]); 24 } 25 } 26 cout<<ans<<endl; 27 return 0; 28 }
4.最大全1矩形
测试地址:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1158
Problem:给出1个M*N的矩阵M1,里面的元素只有0或1,找出M1的一个子矩阵M2,M2中的元素只有1,并且M2的面积是最大的。输出M2的面积。
Key:
解法1:存储矩阵时,将非1的元素存成-inf,直接做一遍最大子矩形和。
解法2:处理每一行的每一个元素能向下延伸的最大全1长度,这样得到每一行都是一个可以“histogram求最大矩形”的序列。主要做法是对于每一行,从左到右遍历一遍刚刚得到的序列,用f[i]表示以i为起点时,向右能扩展的最大矩形面积,最后在所有的f数组中取max。
解法3:递推处理,f[i][j]表示第i行,第j列时,从i能延伸的最大全1长度。记录l和r两个数组,分别表示能向左或右扩展的最大位置。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 using namespace std; 6 const int maxn=1009; 7 int l[maxn],r[maxn]; 8 int f[maxn][maxn],n,m; 9 int main() 10 { 11 12 scanf("%d%d",&n,&m); 13 for(int i=1; i<=n; ++i) 14 for(int j=1; j<=m; ++j) 15 { 16 int x; 17 scanf("%d",&x); 18 if(x==1)f[i][j]=f[i-1][j]+1; 19 else f[i][j]=0; 20 } 21 int ans=0; 22 for(int i=1; i<=n; ++i) 23 { 24 for(int j=1; j<=m; ++j) 25 l[j]=r[j]=j; 26 f[i][0]=f[i][m+1]=-1; 27 for(int j=1; j<=m; ++j) 28 { 29 while(f[i][j]<=f[i][l[j]-1]) 30 l[j]=l[l[j]-1]; 31 } 32 for(int j=m; j>=1; --j) 33 { 34 while(f[i][j]<=f[i][r[j]+1]) 35 r[j]=r[r[j]+1]; 36 } 37 for(int j=1; j<=m; ++j) 38 { 39 if(f[i][j]) 40 { 41 int t=(r[j]-l[j]+1)*f[i][j]; 42 if(t>ans)ans=t; 43 } 44 } 45 } 46 printf("%d ",ans); 47 return 0; 48 }