这道题运用了一种很巧妙的DP方式:悬线法
如图,蓝色为悬线,黄色为向两边延伸的长度
那么显然,最大子矩形的宽一定是这些黄线中最小的(证明从略)
所以我们可以维护三个数组:
Up[i][j]表示向上延伸的长度
Left[i][j]表示向左能延伸到的最远横坐标
Right[i][j]表示向右能延伸到的最远横坐标
Code:
1 #include<bits/stdc++.h> 2 #define INF 0x3f3f3f3f 3 using namespace std; 4 int n,m,maxRec,maxSqr; 5 int mp[2010][2010]; 6 int Up[2010][2010]; 7 int Left[2010][2010]; 8 int Right[2010][2010]; 9 inline void Init(){ 10 cin>>n>>m; 11 for(int i=1;i<=n;i++){ 12 for(int j=1;j<=m;j++){ 13 cin>>mp[i][j]; 14 //预处理一:没啥可说的 15 Left[i][j]=Right[i][j]=j; 16 Up[i][j]=1; 17 } 18 } 19 //预处理二:处理边界 20 for(int i=0;i<=n+1;i++)mp[i][0]=mp[i][m+1]=INF; 21 for(int j=0;j<=m+1;j++)mp[0][j]=mp[n+1][0]=INF; 22 //预处理三:预处理Left、Right数组(黄线) 23 for(int i=1;i<=n;i++){ 24 for(int j=2;j<=m;j++){ 25 if(mp[i][j]!=mp[i][j-1]){ 26 Left[i][j]=Left[i][j-1]; 27 } 28 } 29 } 30 for(int i=1;i<=n;i++){ 31 for(int j=m-1;j>=1;j--){ 32 if(mp[i][j]!=mp[i][j+1]){ 33 Right[i][j]=Right[i][j+1]; 34 } 35 } 36 } 37 } 38 inline void DP(){ 39 for(int i=1;i<=n;i++){ 40 for(int j=1;j<=m;j++){ 41 //更新 42 if(i>1&&mp[i][j]!=mp[i-1][j]){ 43 Up[i][j]=Up[i-1][j]+1; 44 //注意这里存的是坐标所以Left取max而Right取min 45 Left[i][j]=max(Left[i][j],Left[i-1][j]); 46 Right[i][j]=min(Right[i][j],Right[i-1][j]); 47 } 48 //统计矩形,正方形同理 49 int dis=Right[i][j]-Left[i][j]+1; 50 maxRec=max(maxRec,Up[i][j]*dis); 51 maxSqr=max(maxSqr,min(Up[i][j],dis)*min(Up[i][j],dis)); 52 } 53 } 54 cout<<maxSqr<<endl<<maxRec<<endl; 55 } 56 int main(){ 57 Init(); 58 DP(); 59 return 0; 60 }