刷题记录【ZJOI2007棋盘制作】二维DP,悬线法。。。

https://www.luogu.org/problemnew/show/P1169
题目描述
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8×88×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。
而我们的主人公小Q,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。
小Q找到了一张由N×MN imes M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。
不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。
于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?
输入输出格式
输入格式:

包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的N行包含一个N ×M的01矩阵,表示这张矩形纸片的颜色(0表示白色,1表示黑色)。
输出格式:

包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。
第一次接触这种类型的二维dp
因为过于水不得不在阅读过下面这篇论文之后才AC掉
https://blog.csdn.net/clover_hxy/article/details/50532289
自闭了
代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define N 2005 
#define ll long long
#define right wrong
#define left niconico
int f[N][N],height[N][N],left[N][N],right[N][N],n,m,ans1,ans2,map[N][N];
inline int p2(int x){return x*x;}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1 ; i <= n ; i ++)
    { 
    for(int j = 1 ; j <= m ; j ++)
    {
        scanf("%d",&map[i][j]);
        left[i][j]=right[i][j]=j; 
        height[i][j]=1;  
    }
    }
    for(int i = 0; i <= n; i ++ )map[i][0]=-1;
    for(int j = 1; j <= m; j ++ )map[0][j]=-1;
    for(int i = 1; i <= n ; i ++)
    for(int j = 1; j <= m ; j ++)
    {
        if(map[i][j]+map[i][j-1]==1)left[i][j]=left[i][j-1];
    }
    for(int i = 1; i <= n ; i ++)
    for(int j = m; j >= 1 ; j --)
    {
        if(map[i][j]+map[i][j-1]==1)right[i][j-1]=right[i][j];
    }
    for(int i = 1 ; i <= n; i ++)
    for(int j = 1 ; j <= m; j ++)
    {
        if(map[i][j]+map[i-1][j]==1)
        {
            height[i][j]=height[i-1][j]+1;
            left[i][j]=max(left[i][j],left[i-1][j]);
            right[i][j]=min(right[i][j],right[i-1][j]);
        }
    }
    for(int i = 1; i <= n ; i ++)
    for(int j = 1 ; j <= m ; j ++)
    {
        ans1 = max(ans1,height[i][j]*(right[i][j]-left[i][j]+1));
        ans2 = max(ans2,p2(min(height[i][j],right[i][j]-left[i][j]+1)));
    }
    printf("%d
%d",ans2,ans1);
}

最后想一想还是写一下思路吧
首先,悬线法的本质是枚举每一个点为下端点的最大悬线,将悬线左右移动,所能移动的最长区间长度即为最大矩形的一边长。而悬线的长即为另一边长。
由于悬线移动的时间复杂度为O(n)O(n),所以不满足本题要求。为了能在O(1)时间内求出最大区间长,我们可以采用预处理的方法。
用数组left[i][j],right[i][j]表示从i,j开始最靠左、右的符合要求区间终点。
则先递推求出长度为1的悬线对应的left[i][j],right[i][j],
再按照left[i][j]=max(left[i][j],left[i1][j])left[i][j]=max(left[i][j],left[i-1][j])right[i][j]=min(right[i][j],right[i1][j])right[i][j]=min(right[i][j],right[i-1][j])
进行递推
最后枚举所有的i,j
则I,j对应的矩形面积为height[i][j]×(right[i][j]left[i][j]+1)height[i][j] imes(right[i][j]-left[i][j]+1)正方形面积为
min(height[i][j],right[i][j]left[i][j]+1)2min(height[i][j],right[i][j]-left[i][j]+1)^2

原文地址:https://www.cnblogs.com/akonoh/p/10216761.html