luoguP7479 至曾是英雄的您 题解
题意简述
题目大意是给出一些黑棋,需要我们往里面依次放白棋,问是否可以满足无论怎么依次放白棋的过程中黑棋都能存活。
题目分析
看到样例解释,我们可以想到把“白棋恰好将黑棋全部吃掉”的局面画出来。
对于下面这个输入数据:
.*.*.
.***.
.....
我们将白棋用 @
表示:
@*@*@
@***@
.@@@.
观察上面的图像,可以发现此时棋盘中最上面一行的第三个棋子被黑棋包围,称它目前处于“不合法”状态。所以,在依次放置白棋的过程中,这个“不合法”的连通块一定是最后放置,从而将黑棋提掉。
稍微思考一下可以得出:对于每一个白棋的连通块,此时它“不合法”(四周都有黑棋或者边界)的连通块数量必须 \(\leq\) 1。否则,想要到达恰好吃掉黑棋的情况,白棋放置的过程中不能满足同时有两个连通块“不合法”。
也就是说,我们只需要先把恰好能吃掉全部黑棋时的白棋填上,再判断白棋“不合法”的连通块数量是不是大于 1 即可。
代码
此代码的棋盘中 .
用 0
表示, *
用 1
表示, @
用 2
表示。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline ll read(){}
ll T,n,m;
int a[2004][2004]; //棋盘
int ok=0; //统计白棋“不合法”连通块的数量
int dx[4]= {0,0,1,-1},dy[4]= {1,-1,0,0};
bool ff=0; //搜索时的标记
inline void C() //清空
{
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
a[i][j]=0;
ok=0;
}
inline void dfs(int x,int y)
{
if(a[x][y]==1) return;
if(a[x][y]==0) //如果白棋“存活”,标记设为1
{
ff=1;
return;
}
a[x][y]=1;
for(int i=0; i<4; ++i)
{
int ny=dy[i]+y;
int nx=dx[i]+x;
if(ny<=0 || nx<=0 || ny>m || nx>n) continue;
dfs(nx,ny);
}
}
int main()
{
T=read();
while(T--)
{
n=read();
m=read();
C(); //初始化
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) //处理棋盘
{
char ch;
cin>>ch;
if(ch=='.')
a[i][j]=0;
if(ch=='*')
a[i][j]=1;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) //把恰好能吃掉全部黑棋时的白棋填上
{
if(a[i][j]==1)
{
if(a[i-1][j]!=1) a[i-1][j]=2;
if(a[i+1][j]!=1) a[i+1][j]=2;
if(a[i][j-1]!=1) a[i][j-1]=2;
if(a[i][j+1]!=1) a[i][j+1]=2;
}
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
ff=0; //先标记为“不合法”
if(a[i][j]==2)
{
dfs(i,j);
if(ff==0)
ok++; //累加不合法的次数
}
if(ok>=2) //不合法次数 >=2 了,代表黑棋能活
{
puts("YES");
break;
}
}
if(ok>=2) break; //符合条件时及时跳出
}
if(ok<2) puts("NO"); //黑棋不能活
}
return 0;
}
EdisonBa
2021.4.6