Description
\(1\le n,m\le 1000\)。
Solution
下面将 "#" 认为是 1,"." 认为是 0。
可以发现答案是具有二分性的。因此考虑二分答案。
对于答案 \(x\),先找出在 \(x\) 步内向外扩展不会到达的 0 的 1。
然后把这些点加入队列,然后 \(\text{bfs}\),看看经过 \(x\) 轮之后的形状是否与题目一样。
至于如何找出最先加入队列的 1,我们可以先预处理出每个 1 到 0 的距离,然后找那些距离大于 \(x\) 的 1。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
using namespace std;
int n,m,l,r,mid,ans,a[N][N],c[N][N],d[N*N][4];
int fx[5]={0,1,-1,0,0},fy[5]={0,0,0,1,-1};
bool b[N][N];
char ch;
bool check(int x)
{
int h=0,t=0;
memset(d,0,sizeof(d));
memset(b,false,sizeof(b));
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
if (c[i][j]>x)
{
d[++t][1]=i;
d[t][2]=j;
d[t][3]=x;
b[i][j]=true;
}
}
while (h<=t)
{
int x=d[++h][1],y=d[h][2],z=d[h][3];
for (int i=1;i<=4;++i)
{
int xx=x+fx[i],yy=y+fy[i];
if (a[xx][yy]&&!b[xx][yy])
{
b[xx][yy]=true;
if (z-1)
{
d[++t][1]=xx;
d[t][2]=yy;
d[t][3]=z-1;
}
}
}
}
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
if (a[i][j]&&!b[i][j]) return false;
return true;
}
int main()
{
freopen("1ysa93e.in","r",stdin);
freopen("1ysa93e.out","w",stdout);
scanf("%d%d",&n,&m);
ch=getchar();
for (int i=1;i<=n;++i)
{
while (ch!='.'&&ch!='#') ch=getchar();
for (int j=1;j<=m;++j)
{
if (ch=='#') a[i][j]=1;
ch=getchar();
}
}
int h=0,t=1;
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
if (a[i][j]&&!(a[i][j-1]&&a[i][j+1]&&a[i-1][j]&&a[i+1][j])) c[i][j]=1,d[t][1]=i,d[t][2]=j,++t;
while (h<=t)
{
int x=d[++h][1],y=d[h][2];
for (int i=1;i<=4;++i)
{
int xx=x+fx[i],yy=y+fy[i];
if (a[xx][yy]&&!c[xx][yy])
{
d[++t][1]=xx;
d[t][2]=yy;
c[xx][yy]=c[x][y]+1;
}
}
}
l=1;r=min(n,m);
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}