前言:其他题解的解释都那么少,于是我决定发一篇 悬线法 题解
题目大意
给你一个包括X和.的矩阵,让你求出边长最大的不包括X的矩阵
解题思路
动态规划——悬线法
定义:悬线法可以在 O(nm) 的时间复杂度内解决:给定一个NxM的 01矩阵,其面积最大的子矩阵,使得这个子矩阵中的每一位的值都为0或1
时间当然比单调栈要慢很多,但是代码好写就是个板题
需要定义额外的三个数组
left[i][j],right[i][j]和up[i][j]
分别代表当前点(i,j)所能扩展的最左列,最右列和上面可扩展的的最大长度
如何得出这三个数组?
- 初始化,在输入矩阵时,我们就把left和right数组记录当前的列数,up数组记为1
l[i][j]=r[i][j]=j;
up[i][j]=1;
- 我们扫两遍矩阵,一次从前往后扫得到left数组,如果当前列和前一列都为.,那么当前列的left数组就从前一列转移来
考虑为什么?
-
考虑left数组的意义,记录最左可到达的列数
-
我们在初始化时就把left数组记为当前列数,如果这一列和上一列都可以摆放桌子,那么我们当前就可以记录上一列,而上一列同样也是如此记录的,所以我们可以这样递推
for(re i=1;i<=n;i++)
for(re j=2;j<=m;j++)
if(a[i][j]==a[i][j-1]&&a[i][j]=='.')
l[i][j]=l[i][j-1];
然后right数组同理,倒着推一遍就可以了
for(re i=1;i<=n;i++)
for(re j=m-1;j>0;j--)
if(a[i][j]==a[i][j+1]&&a[i][j]=='.')
r[i][j]=r[i][j+1];
最后,AC代码双手奉上(代码中有详细解释)
#include<iostream>
#include<cstdio>
#include<algorithm>
#define re register int
#define N 1010
using namespace std;
char a[N][N]; int n,m,ans;
int l[N][N],r[N][N],up[N][N];
int main() {
scanf("%d%d",&n,&m);
for(re i=1;i<=n;i++) {
for(re j=1;j<=m;j++) {
cin>>a[i][j];//初始化
l[i][j]=r[i][j]=j;
up[i][j]=1;
}
}
for(re i=1;i<=n;i++)
for(re j=2;j<=m;j++)
if(a[i][j]==a[i][j-1]&&a[i][j]=='.')
l[i][j]=l[i][j-1];//求left数组
for(re i=1;i<=n;i++)
for(re j=m-1;j>0;j--)
if(a[i][j]==a[i][j+1]&&a[i][j]=='.')
r[i][j]=r[i][j+1];//求right数组
for(re i=1;i<=n;i++) {
for(re j=1;j<=m;j++) {//不能是第一列,并且都可以放桌子
if(i>1&&a[i][j]==a[i-1][j]&&a[i][j]=='.') {
l[i][j]=max(l[i][j],l[i-1][j]);
r[i][j]=min(r[i][j],r[i-1][j]);
up[i][j]=up[i-1][j]+1;
}
int a=r[i][j]-l[i][j]+1;//a是长
ans=max(ans,a*2+up[i][j]*2);//up[i][j]是当前的宽
}
}
printf("%d",ans-1);//因为自己不算人
return 0;
}
悬线法题目推荐:P1169 [ZJOI2007]棋盘制作 P4147 玉蟾宫 P2701 [USACO5.3]巨大的牛棚Big Barn
后言:悬线法我昨天晚上刚学,今天模拟赛第二题就出了,因为我急着在lsc大佬前面作对一道题,所以没算空间,内存超限,整个人炸裂
点个赞再走好吗