首页 > 其他分享 >洛谷题单指南-常见优化技巧-P4147 玉蟾宫

洛谷题单指南-常见优化技巧-P4147 玉蟾宫

时间:2024-08-15 16:27:48浏览次数:10  
标签:遍历 玉蟾 int 洛谷题 高度 一行 悬线 P4147 递推

原题链接:https://www.luogu.com.cn/problem/P4147

题意解读:找到一个只包含'F'的最大的子矩形。

解题思路:

方法1:设R为0,F为1,先计算二维前缀和,再枚举所有子矩形左上角(x1,y1)、右下角(x2,y2),计算子矩形的区间和,更新最大值,只能得到部分分。

方法2:

对于二维矩阵每个点,定义三个属性:h[][],l[][],r[][]

h表示该点最大悬线高度,所谓悬线,如图

点(2,3)的悬线是从(2,3)往上一共有多少个F,所有h[2][3] = 2, h[5][4] = 5

如何计算h[][]?

可以通过递推:

h[i][j] =h[i-1][j] +1; //每个点的悬线高度等于上一个点的悬线高度+1

知道了每个点往上最大的高度,还需要知道每个点这个高度能到左、右什么位置,所以l[][], r[][]就分别表示某个点的悬线能到的最左、最右的坐标。

有了这些属性,只需要枚举每一个点,计算h * (r - l + 1) * 3,即可得到最大的结果。

那么,问题就剩下如何计算l[][], r[][]?

对于l[][]

第一步:遍历每一行,每一行从左到右遍历,将l[i][j]设置为从(i,j)能到的最左边的F的纵坐标,可以通过递推得到

    for(int i = 1; i <= n; i++)
    {
        for(int j = 2; j <= m; j++)
        {
            if(a[i][j] == 'F' && a[i][j - 1] == 'F')
            {
                l[i][j] = l[i][j - 1]; //在每一行能到的最左边的F,如果相邻两个点都是F,则为左边点的l值
            }
        }
    }

第二步:遍历每一行,每一行从左到右遍历,将l[i][j]更新为(i,j)的悬线能到的最左边的纵坐标(所谓悬线能到,是指从(i,j)往左的悬线高度都不低于h[i][j]),同样可以通过递推得到

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(a[i][j] == 'F' && a[i - 1][j] == 'F')
            {
                l[i][j] = max(l[i][j], l[i - 1][j]); //每个点的悬线能到的最左边,如果上一个点l值更大,则更新为上一个点的l
            }
        }
    }

对于r[][]

第一步:遍历每一行,每一行从右到左遍历,将l[i][j]设置为从(i,j)能到的最右边的F的纵坐标,可以通过递推得到

    for(int i = 1; i <= n; i++)
    {
        for(int j = m - 1; j >= 1; j--)
        {
            if(a[i][j] == 'F' && a[i][j + 1] == 'F')
            {
                r[i][j] = r[i][j + 1]; //在每一行能到的最右边的F,如果相邻两个点都是F,则为右边点的l值
            }
        }
    }

第二步:遍历每一行,每一行从左到右遍历,将r[i][j]更新为(i,j)的悬线能到的最右边的纵坐标(所谓悬线能到,是指从(i,j)往右的悬线高度都不低于h[i][j]),同样可以通过递推得到

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(a[i][j] == 'F' && a[i - 1][j] == 'F')
            {
                r[i][j] = min(r[i][j], r[i - 1][j]); //每个点的悬线能到的最右边,如果上一个点r值更小,则更新为上一个点的r
            }
        }
    }

 100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005;
int n, m, ans;
char a[N][N];
int h[N][N]; //每个点是F且能向上达到最大高度(悬线高度)
int l[N][N]; //每个点的悬线能到达的最左端点
int r[N][N]; //每个点的悬线能达到的最右端点

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            cin >> a[i][j];
            if(a[i][j] == 'F') 
            {
                h[i][j] = h[i - 1][j] + 1; //每个点的悬线高度等于上一个点的悬线高度+1
                l[i][j] = r[i][j] = j; //初始化
            }
            else  h[i][j] = 0;
        }
    }

    for(int i = 1; i <= n; i++)
    {
        for(int j = 2; j <= m; j++)
        {
            if(a[i][j] == 'F' && a[i][j - 1] == 'F')
            {
                l[i][j] = l[i][j - 1]; //在每一行能到的最左边的F,如果相邻两个点都是F,则为左边点的l值
            }
        }
        for(int j = m - 1; j >= 1; j--)
        {
            if(a[i][j] == 'F' && a[i][j + 1] == 'F')
            {
                r[i][j] = r[i][j + 1]; //在每一行能到的最右边的F,如果相邻两个点都是F,则为右边点的l值
            }
        }
    }

    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(a[i][j] == 'F' && a[i - 1][j] == 'F')
            {
                l[i][j] = max(l[i][j], l[i - 1][j]); //每个点的悬线能到的最左边,如果上一个点l值更大,则更新为上一个点的l
                r[i][j] = min(r[i][j], r[i - 1][j]); //每个点的悬线能到的最右边,如果上一个点r值更小,则更新为上一个点的r
            }
            ans = max(ans,  3 * (r[i][j] - l[i][j] + 1) * h[i][j]);
        }
    }
    cout << ans;

    return 0;
}

 

标签:遍历,玉蟾,int,洛谷题,高度,一行,悬线,P4147,递推
From: https://www.cnblogs.com/jcwy/p/18361265

相关文章

  • 洛谷题单指南-常见优化技巧-P1115 最大子段和
    原题链接:https://www.luogu.com.cn/problem/P1115题意解读:最大连续子序列的和。解题思路:DP的做法可参考:https://www.cnblogs.com/jcwy/p/18144124也可以采用双指针来枚举:i从1开始,j=i用j来枚举连续序列,如果已有序列和+下一个a[j]>=下一个a[j],那个j一直++,累加序列和如果出......
  • 洛谷题单指南-常见优化技巧-P1638 逛画展
    原题链接:https://www.luogu.com.cn/problem/P1638题意解读:在n个数中,选出a、b两个端点,使得a~b之间不同的数字为m,且b-a最小。解题思路:要寻找最小的包括所有数字的区间,可以采用双指针算法1、设i,j分别是左右指针2、如果当前区间内不同数字个数不到m,j往后移3、记录数字个数到m时......
  • 洛谷题单指南-前缀和差分与离散化-P1904 天际线
    原题链接:https://www.luogu.com.cn/problem/P1904题意解读:给出(左端点,高度,右端点)表示的若干建筑,要输出其轮廓,所谓轮廓就是每个点被覆盖的最高建筑的高度所描绘的线。解题思路:如果能计算每个点被覆盖的最高建筑的高度,用数组h[10005]保存,那么输出轮廓的过程只需要枚举每一个点,如......
  • 洛谷题单指南-前缀和差分与离散化-P3029 [USACO11NOV] Cow Lineup S
    原题链接:https://www.luogu.com.cn/problem/P3029题意解读:不同的坐标位置有不同种类的牛,要计算一个最小的区间,包括所有种类的牛。解题思路:由于坐标位置不连续,并且数值范围较大,因此需要离散化处理,将坐标处理成1~n连续分布由于种类编号数值范围也比较大,也需要离散化处理,去重后的......
  • 洛谷题单指南-前缀和差分与离散化-P4552 [Poetize6] IncDec Sequence
    原题链接:https://www.luogu.com.cn/problem/P4552题意解读:对一组数字序列,进行若干次区间+1或者-1操作,最终使得所有数字一样,计算最少的操作次数,以及能得到多少种不同序列。解题思路:要使得序列每一个数字都相同,则其差分除了第一项之外其余项都是0。因此,问题转化为:给定一个差分数......
  • 洛谷题单指南-前缀和差分与离散化-P2882 [USACO07MAR] Face The Right Way G
    原题链接:https://www.luogu.com.cn/problem/P2882题意解读:一个有F、B组成的序列,每次可以翻转k个连续子序列,翻转:F->B,B->F,计算最少翻转多少次可以将序列都变成F,并求相应的k。解题思路:为方便处理,设F为1,B为01、朴素做法枚举k:1~n  枚举序列,一旦遇到0,就将连续k个字符翻转,如果可......
  • 洛谷题单指南-前缀和差分与离散化-P1083 [NOIP2012 提高组] 借教室
    原题链接:https://www.luogu.com.cn/problem/P1083题意解读:已知第i天有r[i]个教室可以供租借,有m个租借教室的订单,第i订单需要在第s[i]~t[i]天区间内租借d[i]个教室,计算是否全部订单都能满足,如果不满足要输出从第几个订单开始不满足。解题思路:1、朴素做法枚举1~m个订单,通过差分......
  • 洛谷题单指南-前缀和差分与离散化-P3406 海底高铁
    原题链接:https://www.luogu.com.cn/problem/P3406题意解读:1-n个城市共了n段路,第i段路不买卡票价a[i],买卡票价b[i](卡本身花费c[i]),给定一个路程顺序,计算最少的通行费用。解题思路:根据路程,计算每段路各走了多少次,然后对于每段路,计算买卡和不买卡两种花费,取较小的累加即可。如何......
  • 洛谷题单指南-前缀和差分与离散化-P3017 [USACO11MAR] Brownie Slicing G
    原题链接:https://www.luogu.com.cn/problem/P3017题意解读:将一个r*c的矩阵,横向切成a条,每一条纵向切除b块,计算每一块子矩阵之和的最小值最大是多少。解题思路:要计算最小值中最大的,直觉上可以采用二分,下面来分析单调性:给定一个子矩阵块之和的值,值越小可以划分的条数、块数就越多......
  • 洛谷题单指南-前缀和差分与离散化-P2004 领地选择
    原题链接:https://www.luogu.com.cn/problem/P2004题意解读:在一个n*m的矩阵中,找到边长为c的正方形,使得正方形范围内的数之和最大,输出正方形的左上角坐标。解题思路:二维前缀和的简单应用第一步:计算二维前缀和第二步:枚举边长为c的正方形左上角,计算正方形区域的数之和,更新答案第......