HDU 2870 (最大0 1 矩阵面积)

本文介绍了一种解决最大相同子矩阵问题的算法,通过将可转换字符视为目标字符,计算不同情况下能形成的最大子矩阵面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2870


这个题是  1505,1506的变形,建议先去看看1506,那是基础,解释都在代码中,感觉关键还是掌握下面我强调的难点处(对我还说是难点)



Problem Description
Now here is a matrix with letter 'a','b','c','w','x','y','z' and you can change 'w' to 'a' or 'b', change 'x' to 'b' or 'c', change 'y' to 'a' or 'c', and change 'z' to 'a', 'b' or 'c'. After you changed it, what's the largest submatrix with the same letters you can make?
 

Input
The input contains multiple test cases. Each test case begins with m and n (1 ≤ m, n ≤ 1000) on line. Then come the elements of a matrix in row-major order on m lines each with n letters. The input ends once EOF is met.
 

Output
For each test case, output one line containing the number of elements of the largest submatrix of all same letters.
 

Sample Input
2 4 abcw wxyz
 

Sample Output
3
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 1005

int h[N][N],le[N],ri[N];
char a[N][N];

int main()
{
    int n,m,i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        getchar();

        char c;
        int ans=0,t; 

        for(i=1;i<=n;i++)
        {
            for(j=1;j<=m;j++)
               scanf("%c",&a[i][j]);
            getchar();
        }

        for(t=1;t<=3;t++)      //分三种情况,a,(b,c),当可以把转化a的字符全部转化成a(不是真的变成a),
                                //计算高度,求得最大矩形面积(方法详解在上篇 1505)
        {                      //然后比较这三种情况的最大值
            memset(h,0,sizeof(h));

            if(t==1)
            {
                for(i=1;i<=n;i++)
                    for(j=1;j<=m;j++)
                      if(a[i][j]=='a'||a[i][j]=='w'||a[i][j]=='y'||a[i][j]=='z')
                          h[i][j]=h[i-1][j]+1;
                      else
                          h[i][j]=0;
            }
            else
                if(t==2)
            {
                 for(i=1;i<=n;i++)
                    for(j=1;j<=m;j++)
                      if(a[i][j]=='b'||a[i][j]=='w'||a[i][j]=='x'||a[i][j]=='z')
                          h[i][j]=h[i-1][j]+1;
                      else
                          h[i][j]=0;
            }
            else
            {
                for(i=1;i<=n;i++)
                    for(j=1;j<=m;j++)
                      if(a[i][j]=='c'||a[i][j]=='x'||a[i][j]=='y'||a[i][j]=='z')
                          h[i][j]=h[i-1][j]+1;
                      else
                          h[i][j]=0;
            }

            for(i=1;i<=n;i++)    //以上高度确定,下面是算面积
            {

                for(j=1;j<=m;j++)
                {
                    le[j]=j;
                    while(le[j]>1&&h[i][le[j]-1]>=h[i][j])  //此处是难点,不懂看 上篇1505
                        le[j]=le[le[j]-1];
                }

                for(j=m;j>=1;j--)
                {
                    ri[j]=j;
                    while(ri[j]<m&&h[i][ri[j]+1]>=h[i][j])   //此处是难点,不懂看 上篇1505
                        ri[j]=ri[ri[j]+1];
                }

                for(j==1;j<=m;j++)
                  ans=max(ans,(ri[j]-le[j]+1)*h[i][j]);
            }
      }
        printf("%d\n",ans);
    }
    return 0;
}

//感觉还是很神奇的方法,我也是看别人好久才懂得,


按照刚刚同样的格式整理总结一下以下内容:幻灯片1 矩阵前缀和 幻灯片2 复习: 前缀和算法  对于一个长为n的序列a = {a[1],a[2],a[3],....,a[n]}  我们可以求出a的前缀和数组s,其中s[i] = a[1]+a[2]+...+a[i]  这样当我们想要求a序列中一段区间的和时,就可以用s[r]-s[l-1]求出a[l]+a[l+1]+...+a[r]的区间和 幻灯片3 问题探究  在一个矩阵上,如果我们想要求出一个子矩阵的和,是否有类似于前缀和的方法?  提示,考虑一下如何定义矩阵上的“前缀” ? 幻灯片4 求面积 思考: 下图的大矩形S,被划分出了四个子矩形A,B,C,D  请用A,B,C,D的面积,进行四则运算,得到大矩形的面积  请用S,A,B,C的面积,通过四则运算,得到矩形D的面积 幻灯片5 启发 通过上面的例子可以想到:  当我们想要算出矩阵中某一块子矩阵的和,例如想要得到矩形D的和,可以先提前求出S,A,B,C的和(就像在一维前缀和算法中求sum[]数组一样)  再通过S-A-B+C得到D的矩阵和  要提前求出S,A,B,C这类矩形的和,就要先归纳出他们的特点。  思考: S,A,B,C这些矩形有什么共性呢? 幻灯片6 前缀矩阵 很容易发现,S、A、B、C都是以矩阵中的某个元素为右下角,以矩阵第一行第一列为左上角的  我们把这些矩阵称为前缀矩阵,在二维前缀和算法中,就是要提前求出sum[i][j]表示以第i行第j列为右下角的前缀矩阵和  下图这些子矩阵就是前缀矩阵: 幻灯片7 练一练 sum[i][j]表示以第i行第j列为右下角的前缀矩阵和  对于右侧矩阵:    求出: sum[2][3] sum[4][2] sum[3][5] 幻灯片8 预处理 想要快速求出所有的前缀矩阵和sum[i][j],就要类似一维前缀和那样找到相应的递推公式,像动态规划一样快速的求出所有的sum值  看看下面的矩阵,S = C+B-A+D ,替换成sum值和矩阵a中的元素就是: sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] + a[i][j]      这就是矩阵前缀和的递推公式,请认真理解并记忆 幻灯片9 预处理—代码实现  读入n,m和n*m的矩阵,求出sum[][]数组,并将其输出  输入样例: 输出样例:     sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] + a[i][j]   幻灯片10 预处理-代码实现 幻灯片11 求出子矩阵的和 看下面一个例子,如何利用sum数组求出矩形D,也就是以(4,4)为左上角、(5,6)为右下角的的子矩阵和           幻灯片12 求出子矩阵的和 看下面一个例子,如何利用sum数组求出矩形D,也就是以(4,4)为左上角、(5,6)为右下角的的子矩阵和        D = S - B - C + A = sum[5][6] - sum[5][3] - sum[3][6] + sum[3][3]   幻灯片13 求出子矩阵的和 更普遍的,如果想要求出以(a,b)为左上角,(c,d)为右下角的子矩阵和  就可以用sum[c][d]-sum[a-1][d]-sum[c][b-1]+sum[a-1][b-1]  例如右图,矩阵D中: 左上角为(4,4),右下角为(5,6)矩阵D的和为sum[5][6]-sum[3][4]-sum[4][3]+sum[3][3]   幻灯片14 例题: 1717 最大矩阵 幻灯片15 暴力算法1 简单粗暴的处理方法是:  用双重循环枚举子矩阵左上角(a,b)的坐标  利用双重循环计算这个子矩阵的和  这样做的时间复杂度为O(n^4)  代码实现略,课后布置成作业,每位同学完成并提交后,在群里截图打卡“暴力算法1已完成”   幻灯片16 暴力算法2 简单粗暴的处理方法是:  用双重循环枚举子矩阵左上角(a,b)的坐标  利用一维前缀和,O(n)枚举子矩阵的每一行求和  这样做的时间复杂度为O(n^3)  代码实现略,课后布置成作业,每位同学完成并提交后,在群里截图打卡“暴力算法2已完成”   幻灯片17 标准解法 利用二维前缀和,我们在处理出sum[][]数组后,只要:  枚举子矩阵的左上角(a,b)的坐标,求出右下角(c = a+x-1, d = b+y-1)  利用二位前缀和直接求出子矩阵的和  sum[c][d]-sum[c][b-1]-sum[a-1][d]+sum[a-1][b-1]  幻灯片18 参考核心代码 幻灯片19 例题: 1722 星空 幻灯片20 例题: 1722 星空 幻灯片21 题解  简单分析,会发现每过c+1秒所有星星的亮度又变回来了  所以t时刻是等价于t%(c+1)时刻的  这样的话实际上只有0~c这c+1个不同的时刻  可以用sum[t][][]记录t时刻的星星亮度对应的矩阵前缀和,这样的预处理的复杂度是O(c*n*m)的,对于每次询问,只要计算出等价的0~c中的时刻,并计算矩阵和即可。 幻灯片22 例题 1724 Pond 幻灯片23 题解 二分中位数mid,将大于mid的数设为1,否则设为0  这样一个子矩阵的和就是这个子矩阵中大于mid的数的个数  枚举k*k的正方形子矩阵的右下角,并利用sum数组计算对应的矩阵和  如果找到一个子矩阵的和是≤k*k/2的,说明存在一个正方形子矩阵的中位数≤mid,就朝着小的方向二分,否则朝着大的方向二分 幻灯片24 核心代码 幻灯片25 作业  例题/中等难度题目 1717 1722 1724  较难题目 17201721
最新发布
03-08
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值