ZJOI2007 棋盘制作

传送门

DP找给定区域内最大符合条件的矩形/正方形。这里有一种新的方法———悬线法。

悬线的定义:每个点(i,j),都对应一条悬线,当前点是悬线的下端,悬线的上端为一个障碍点或者矩形的上边界。

所以一个符合条件的矩形,我们只要使用悬线法计算出这条悬线移动到不合法位置时的边界即可。

(注意left和right似乎在iostream库中是保留字)

用lef[i][j]表示从点(i,j)引出的悬线能拓展到的最左边的位置,righ[i][j]表示从点(i,j)引出的悬线能拓展到最右边的位置。up[i][j]表示这条悬线能向上拓展的最大长度。

这样我们可以先预处理出每条悬线的lef,righ,只要左右两格不同就可以进行转移。

之后我们就得到递推式:

left[i][j]=max(left[i][j],left[i-1][j];
right[i][j]=min(right[i][j],right[i-1][j];
注意这里要考虑上一行的情况,是因为up的原因。
之后从上向下DP,每次处理一下当前的最大合法矩形即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
//#include<iostream>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 50005;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int n,m,g[2005][2005],up[2005][2005],lef[2005][2005],right[2005][2005];
int ans1,ans2;

int main()
{
    n = read(),m = read();
    rep(i,1,n)
    rep(j,1,m) g[i][j] = read(),lef[i][j] = right[i][j] = j,up[i][j] = 1;
    rep(i,1,n)
    rep(j,2,m) if(g[i][j] != g[i][j-1]) lef[i][j] = lef[i][j-1];
    rep(i,1,n)
    per(j,m-1,1) if(g[i][j] != g[i][j+1]) right[i][j] = right[i][j+1];
    rep(i,1,n)
    rep(j,1,m)
    {
    if(i > 1 && g[i][j] != g[i-1][j])
    {
        lef[i][j] = max(lef[i][j],lef[i-1][j]);
        right[i][j] = min(right[i][j],right[i-1][j]);
        up[i][j] = up[i-1][j]+1;
    }
    int lena = right[i][j] - lef[i][j] + 1;
    int lenb = min(lena,up[i][j]);
    ans1 = max(ans1,lenb*lenb);
    ans2 = max(ans2,lena*up[i][j]);
    }
    printf("%d\n%d\n",ans1,ans2);
    return 0;
}

 

 
posted @ 2018-09-05 14:37  CaptainLi  阅读(141)  评论(0编辑  收藏  举报