洛谷 P1169 题解

本文详细介绍了如何解决一个关于在黑白矩阵中选取特定形状(正方形或长方形)的子矩阵的问题,使得相邻点颜色不同。作者通过解析题意、提供数据示例,并给出思路,讲解了使用悬线DP的方法来解决此问题,包括状态定义、转移方程以及代码实现。

题意简述

请你在一个黑白矩阵中选出一个长方形(含正方形)和正方形的子矩阵,使得所有相邻的点颜色不一样。
矩阵大小:两条边都&lt;=2000&lt;=2000<=2000

数据

输入
3 3
1 0 1
0 1 0
1 0 0
输出
4
6

解释

给定矩阵:
blog1.png
正方形:
blog2.png
长方形:
blog3.png
我的控制台是萌萌哒#ffc0cb粉色
一个小技巧:如果您的控制台是像我的这样能选择的,那么只要按着ALT就可以只选出矩形部分了。
效果图:
blog4.png

思路

悬线DP。我们用L[i][j],R[i][j]L[i][j],R[i][j]L[i][j],R[i][j]分别表示向左,右最远珂以延伸(延伸的条件就是黑白相间)到同一行第几个点U[i][j]U[i][j]U[i][j]表示向上延伸(延伸条件同上)的最大长度。(这是悬线DP的套路
然后转移方程很好写。用mpmpmp记录矩阵,枚举i,ji,ji,jL,RL,RL,R的初始值是jjjUUU的初始值是1.若mp[i][j−1]!=mp[i][j]mp[i][j-1]!=mp[i][j]mp[i][j1]!=mp[i][j],则L[i][j]=L[i][j−1]L[i][j]=L[i][j-1]L[i][j]=L[i][j1]RRR的转移类似。UUU到后面一边DPDPDP一边转移。

那现在就开始DPDPDP了。枚举i,ji,ji,j,表示我们现在选的子矩形的左下角。如果mp[i][j]!=mp[i−1][j]mp[i][j]!=mp[i-1][j]mp[i][j]!=mp[i1][j],说明我们现在枚举的左下角珂以继承上面的结果。显然要先更新UUU,那我们怎么继承其他的部分呢?
blog1.png
绿色表示当前枚举到的点,红色表示珂以延伸的点。
显然,我们现在这一行珂以左到222,右到444,但是上一行到不了这么远。
所以我们得到,在往下继承的时候,左取minminmin,右取maxmaxmax
然后你会发现,这样考虑每个子矩形的左下角,所有都会被考虑到。正确性。。。感性证明一下,理性证明我也不会证。

代码:
(以下代码中的leftleftleft就是LLLrightrightright就是RRRupupup就是UUU)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2010
using namespace std;
int mp[N][N];
int left[N][N],right[N][N],up[N][N];
int n,m;

void Input()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&mp[i][j]);
            left[i][j]=right[i][j]=j;
            up[i][j]=1;//初始化
        }
    }
}

void Build()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=2;j<=m;j++)
        {
            if (mp[i][j-1]!=mp[i][j])
            {
                left[i][j]=left[i][j-1];
            }
        }
    }

    for(int i=1;i<=n;i++)
    {
        for(int j=m-1;j>=1;j--)
        {
            if (mp[i][j+1]!=mp[i][j])
            {
                right[i][j]=right[i][j+1];
            }
        }
    }
    //预处理left和right
}
void Solve()
{
    Build();//也珂以写在main里面,这个代码是一个星期前写的,由于心情的问题,会有一点点风格的影响
    //写代码也要体现自己的情感,就像写诗一样。做到"诗如代码,代码如诗"
    int ans1=0,ans2=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if (i>1 and mp[i][j]!=mp[i-1][j])//继承
            {
                left[i][j]=max(left[i][j],left[i-1][j]);
                right[i][j]=min(right[i][j],right[i-1][j]);
                up[i][j]=up[i-1][j]+1;//更新U
            }
            int a=right[i][j]-left[i][j]+1;
            int b=min(a,up[i][j]);
            ans1=max(ans1,b*b);
            ans2=max(ans2,a*up[i][j]);//更新答案
            //注意正方形和长方形的区别
        }
    }
    printf("%d\n%d\n",ans1,ans2);
}

main()
{
    Input();
    Solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值