1 条题解

  • 0
    @ 2025-8-24 21:45:41

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 潜翎
    退役了,谢谢大家

    搬运于2025-08-24 21:45:41,当前版本为作者最后更新于2019-06-09 02:07:00,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    200引导我们思考O(n^3)。

    O(n^3)的矩阵中的dp最经典的不就是最大子矩阵了么?

    让我们回忆一下最大子矩阵是怎么搞的。

    两层循环枚举起始行和终止行,预处理每一列前缀和。
    一层循环做dp:
    如果仅选择当前列不比选择原结果优,暂时加入原结果。
    如果仅选择当前列更优,舍弃原结果,选择当前列。
    实时更新ans。
    

    然后我们发现这玩意儿和最大子矩阵差不多。

    两层循环枚举起始行和中止行,预处理列的连通性。
    一层循环做dp:
    如果这两行中间的某一列是连通的,说明它有成为一堵纵向墙的资格。
    如果随着列的枚举,这两行的连通性被破坏(之间建不成横向墙),那么前面的纵向墙就作废。
    如果一堵纵向墙前面还有另一堵未被作废的纵向墙,那么可用来更新答案。
    

    我知道看起来云里雾里的,所以放代码吧。

    for(int i=1;i<=m;i++)
    	{
    		int x=0;
    		for(int j=0;j<=n;j++)
    		 if(str[j][i]=='X'||!str[j][i]) x++;
    		 else a[j][i]=x;
    	}
    

    这一段是预处理纵向连通性的。如果一数列中数字一样且非0就连通,0是沼泽。

    用样例打出来的a数组是这样的:

    111111
    110110
    012012
    212212
    210212
    

    对照一下代码应该很好理解吧。

    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
            for(int k=1;k<=m;k++)
    

    枚举起始行中止行。扫过每一列。

    if(str[i][k]!='.'||str[j][k]!='.') l=0;
    

    l是较靠左的纵向墙。

    如果地图中枚举出的这两行中有一片沼泽地的话,这片沼泽地的左侧的墙全都不能为这两行接下来的答案做出贡献了。所以左墙设为空。

    if(a[i][k]==a[j][k]&&a[i][k])
    {
            if(!l) l=k;
    	else r=k,len=max(r-l+1,len);
    }
    

    如果满足a[i][k]==a[j][k]则纵向连通,但要排除a[i][k]==a[j][k]=0的情况(这样就是两片沼泽)。

    这说明这儿有资格建纵向墙。

    如果没有左侧的墙就赶紧把它设为左侧的墙。

    我们是从左向右枚举的,我们自然希望较靠右的墙越靠右越好,所以如果左侧墙存在,我们可以直接把这列设为右侧墙,并且答案可更新。

    最后乘一乘输出就好了。

    完整代码奉上:

    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #define N 210
    using namespace std;
    int n,m,a[N][N],f[N],ans;
    char str[N][N];
    int main()
    {
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++)
    	 scanf("%s",str[i]+1);
    	for(int i=1;i<=m;i++)
    	{
    		int x=0;
    		for(int j=0;j<=n;j++)
    		 if(str[j][i]=='X'||!str[j][i]) x++;
    		 else a[j][i]=x;
    	}
    	for(int i=1;i<n;i++)
    	 for(int j=i+1;j<=n;j++)
    	 {
    	 	int len=0,l=0,r=0;
    	 	memset(f,0,sizeof(f));
    	 	for(int k=1;k<=m;k++)
    	 	{
    	 		if(str[i][k]!='.'||str[j][k]!='.') l=0;
    	 		if(a[i][k]==a[j][k]&&a[i][k])
    	 		{
    	 			if(!l) l=k;
    	 			else r=k,len=max(r-l+1,len);
    			}
    		}
    		ans=max(ans,(j-i+1)*len);
    	 }
    	 printf("%d",ans);
    	 return 0;
    }
    

    后记:

    这是我第一次不看题解(好吧,以前看过,不过我没记住题解内容,也算独立完成吧)写蓝题dp,我一直不擅长dp,居然把这个写出来了,有点自豪。

    我做题之前瞄了一眼题解数量,5篇,心想自己是不是也能写题解试试。

    然后看了一眼范围,n m<=200,心里忽然飘过一句话“200引导我们思考O(n^3)。”感觉是哪个题解里见过,觉着挺乐呵。

    做完题,习惯性打开题解。

    第一句话。

    “200引导我们思考O(n^3)。”

    我忽然就真想动动手写题解了。

    • 1

    信息

    ID
    2199
    时间
    1000ms
    内存
    125MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者