最大矩形问题总结

昨天晚上刷水题写到一道最大全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 }
Problem#1

 

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 }
Problem#2

 

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 }
Problem#3--解法1

  解法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 }
Problem#3--解法2

 

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\n",ans);
47     return 0;
48 }
Problem#4--解法3

 

posted @ 2017-10-19 10:40  BK-Edwina  阅读(1713)  评论(0编辑  收藏  举报