最大矩形問題總結


昨天晚上刷水題寫到一道最大全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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM