题目描述
给定一个n*m的矩阵,矩阵元素由X和O构成,请求出其中最大的由X构成的蝴蝶形状。
由X构成的蝴蝶形状的定义如下:
存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。
例如:
XOOOX
XXOXX
XOXOX
XXOXX
XOOOX
是一个X构成的蝴蝶形状。
X
也是。
而
XOOX
OXXO
OXXO
XOXX
不是(不存在中心点)。
由X构成的蝴蝶形状的定义如下:
存在一个中心点,并且其往左上、左下、右上、右下四个方向扩展相同的长度(扩展的长度上都是X),且左上顶点与左下顶点、右上顶点与右下顶点之间的格子全由X填充。我们不在意在蝴蝶形状内部是X还是O。
例如:
XOOOX
XXOXX
XOXOX
XXOXX
XOOOX
是一个X构成的蝴蝶形状。
X
也是。
而
XOOX
OXXO
OXXO
XOXX
不是(不存在中心点)。
输入描述:
第一行两个整数n, m表示矩阵的大小(1 <= n, m <= 2000);
接下来n行,每行一个长度为m的字符串表示矩阵,矩阵元素保证由X和O构成。
输出描述:
一行一个整数表示最大的由X构成的蝴蝶形状的对角线的长度。
示例1
输入
5 5
XOOOX
XXOXX
XOXOX
XXOXX
XOOOX
输出
5
思路:
很巧妙的题目转化方式!
首先O(nm)预处理三个量:
dp[0][i][j]:从(i,j)的位置开始左下方向连续的X的个数。
dp[1][i][j]:从(i,j)的位置开始向下连续的X的个数。
dp[2][i][j]:从(i,j)的位置开始右下方向连续的X的个数。
考虑枚举每一个蝴蝶形状的左上角顶点位置(i,j)。
则我们需要找到一个最大的k,使(i,k)为一个蝴蝶形状的右上顶点位置。
当蝴蝶形状合法时,k需要满足的条件为:(蝴蝶形状的长为k−j+1)
1).min(dp[1][i][j],dp[2][i][j])>=k−j+1
2).min(dp[0][i][k],dp[1][i][k])>=k−j+1
3).k>=j
移项后得到:
1).j<=k<=min(dp[1][i][j],dp[2][i][j])+j−1
2).min(dp[0][i][k],dp[1][i][k])−k−1>=−j
因为需要最大化k,故可以考虑用线段树维护,对于每一个k,线段树的相应节点保存min(dp[0][i][k],dp[1][i][k])−k−1。故题目便转化成了在区间(j,min(dp[1][i][j],dp[2][i][j])+j−1)中找到最大的下标使其维护的值>=−j。
因为蝴蝶形状有中心点,故长度k−j+1一定为奇数,需要两个线段树,对于奇偶性不同的k分开维护。
原址:点击打开链接
代码:
#include<bits/stdc++.h>
#define N 2005
using namespace std;
const int inf=1e9+7;
char s[N][N];
int dp[N][N][3];
struct Tree{
int tree[N*4];
void build(int l,int r,int p)
{
tree[p]=-inf;
if(l==r)return;
int m=l+r>>1;
build(l,m,p<<1);
build(m+1,r,p<<1|1);
}
void update(int l,int r,int p,int x,int y)
{
if(l==r){
tree[p]=y;
return;
}
int mid=l+r>>1;
if(x<=mid)update(l,mid,p<<1,x,y);
else update(mid+1,r,p<<1|1,x,y);
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
int query(int l,int r,int x,int L,int R,int p)
{
if(L==R)return R;
int mid=L+R>>1,ans=0;
if(r>mid&&tree[p<<1|1]>=x)
ans=max(ans,query(l,r,x,mid+1,R,p<<1|1));
if(l<=mid&&tree[p<<1]>=x)
ans=max(ans,query(l,r,x,L,mid,p<<1));
return ans;
}
};
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=n;i;i--){
for(int j=1;j<=m;j++){
if(s[i][j]=='X'){
dp[i][j][0]=dp[i+1][j-1][0]+1;
dp[i][j][1]=dp[i+1][j][1]+1;
dp[i][j][2]=dp[i+1][j+1][2]+1;
}
}
}
int ans=0;
Tree t1,t2;
for(int i=1;i<=n;i++){
t1.build(1,m,1);
t2.build(1,m,1);
for(int j=1;j<=m;j++){
if(j&1)t1.update(1,m,1,j,min(dp[i][j][0],dp[i][j][1])-j-1);
else t2.update(1,m,1,j,min(dp[i][j][0],dp[i][j][1])-j-1);
}
for(int j=1;j<=m;j++){
if(s[i][j]!='X')continue;
int x=min(dp[i][j][1],dp[i][j][2]);
if(x<=ans)continue;
if(j&1)x=t1.query(j,x+j-1,-j,1,m,1);
else x=t2.query(j,x+j-1,-j,1,m,1);
ans=max(ans,x-j+1);
}
}
printf("%d\n",ans);
return 0;
}