这里介绍悬线法。
原题
笔者注:笔者在学习了悬线法之后不免存在一些疑问,留给后续探讨和思考。
- 代码的实现部分是否存在没有判断仅仅存在一行的情况?然而通过了luogu的数据。
1.思想
悬线法的核心思想是计算出对于当前一行能够向左/向右可以取到的最大值,处理出闭区间后并考虑出这段区间长度可以向上扩展出的最大高度是多少,每次计算更新当前能达到的区间,取出最大值,进行计算;对于一个非障碍格,可以延申出一条一直往上延伸的,碰到障碍格或上边界则停止的线,这条线往往被称之为"悬线"。我们的目的就是模拟这个过程实现。
2.实现
一个奶龙的做法是直接暴力,不再赘述。
考虑预处理出每一行能够达到的最左和最右。
令:
\(l_{i,j}\)表示对于\((i,j)\)这一个位置这一行最多可以向左扩展到的格子,
\(r_{i,j}\)表示对于\((i,j)\)这一个位置这一行最多可以向右扩展到的格子。
\(h_{i,j}\)表示悬线的最大长度。
接下来,对于每一个非障碍格,计算他的悬线可以往左或往右的最远情况,当这个格子是非障碍格,且他的上方也是,如果上方的\(l\)值更大,则更新为上方的\(l\)值;\(r\)数组同理,如果上方的\(r\)值更小,则更新为上方的\(r\)值,\(h\)数组的更新则每次找到一个上方的就\(+1\),无需考虑\(l\)与\(r\)。对于初始化部分,停下自行思考一下,较为简单。这样就好办了,只需要代码实现就可以了,贴一份。
3.代码
# include <bits/stdc++.h>
using namespace std;
const int N = 1010;
char s[N][N];
int l[N][N],r[N][N],h[N][N];
int main (){int n,m;scanf("%d%d",&n,&m);for(int i = 1;i <= n;i++){for(int j = 1;j <= m;j++){cin >> s[i][j];h[i][j] = 1,l[i][j] = r[i][j] = j;}}for(int i = 1;i <= n;i++){for(int j = 2;j <= m;j++){if(s[i][j] == 'F' && s[i][j-1] == 'F'){l[i][j] = l[i][j-1];}}for(int j = m-1;j >= 1;j--){if(s[i][j] == 'F' && s[i][j+1] == 'F'){r[i][j] = r[i][j+1];}}}int mx = 0;for(int i = 1;i <= n;i++){for(int j = 1;j <= m;j++){if(i > 1 && s[i][j] == 'F'){if(s[i-1][j] == 'F'){h[i][j] = h[i-1][j]+1;if(l[i][j] < l[i-1][j]){l[i][j] = l[i-1][j];}if(r[i][j] > r[i-1][j]){r[i][j] = r[i-1][j];}}mx = max(mx,(r[i][j]-l[i][j]+1)*h[i][j]);}}}int ans = mx*3;printf("%d\n",ans);return 0;
}
4.个人总结
主要的错误点在于对于\(mx\)那里的计算出了问题,我将他放在了第二个\(if\)里面,这显然是错误的,因为我们没有考虑到只针对于答案恰好是一行的某一段\(F\),然而数据较为乐观,我们拿下了\(92pts\),如果放在里面就在程序的最后跑一边最大值就ok了。同样这样似乎也可以解决我在开头提出的问题,思路较为清晰。