[洛谷P1169][题解][ZJOI2007]棋盘制作

我不是题目的说

这道题运用了一种很巧妙的DP方式:悬线法

如图,蓝色为悬线,黄色为向两边延伸的长度

那么显然,最大子矩形的宽一定是这些黄线中最小的(证明从略)

所以我们可以维护三个数组:

Up[i][j]表示向上延伸的长度

Left[i][j]表示向左能延伸到的最远横坐标

Right[i][j]表示向右能延伸到的最远横坐标

Code:

 1 #include
 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<endl;
55 }
56 int main(){
57     Init();
58     DP();
59     return 0;
60 }

 

[洛谷P1169][题解][ZJOI2007]棋盘制作_第1张图片

你可能感兴趣的:([洛谷P1169][题解][ZJOI2007]棋盘制作)