题目大意:
你有一个n*m的矩形,一开始所有格子都是白色,然后给出一个目标状态的矩形,有的地方是白色,有的地方是黑色,你每次可以选择一个连通块(四连通块,且不要求颜色一样)进行染色操作(染成白色或者黑色)。问最少操作次数。
思路:
可以证明每一次操作的范围都是上一次操作的子集,并且颜色与上一次相反(但是我不会证)。
于是我们可以把黑色连通块和白色连通块之间互相连边,这样就形成了一个图,我们枚举最后进行操作的那个区域,那么可以看成是以当前这个连通块为中心,不断地将最外围的连通块染上题目要求的颜色。
于是我们只需要枚举最后的区域
S
i
S_i
Si,求出每个连通块
T
j
T_j
Tj和这个区域
S
i
S_i
Si的最短距离(也就是这个连通块
T
j
T_j
Tj的操作次数),然后取所有的
T
j
T_j
Tj的距离最大值为当前中心
S
i
S_i
Si的操作次数
v
a
l
i
val_i
vali,最后求出
min
(
v
a
l
i
)
\min(val_i)
min(vali)即可。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2638.in","r",stdin);
freopen("bzoj2638.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=50+10;
const int maxm=2500+10;
int n,m,a[maxn][maxn],id[maxn][maxn];
int dx[5]={0,1,0,-1,0};
int dy[5]={0,0,1,0,-1};
int col[maxm],beg[maxm],las[maxm<<2],to[maxm<<2],cnte;
int dis[maxm],ans=0x3f3f3f3f;
bool vis[maxm];
bool judge(int x,int y){
return x>=1 && x<=n && y>=1 && y<=m;
}
void add(int u,int v){
las[++cnte]=beg[u];beg[u]=cnte;to[cnte]=v;
}
queue<int>qu;
int bfs(int ss){
int ret=0;
memset(vis,0,sizeof(vis));
memset(dis,63,sizeof(dis));
qu.push(ss); vis[ss]=1; dis[ss]=0;
while(!qu.empty()){
int u=qu.front(); qu.pop(); vis[u]=0;
for(int i=beg[u];i;i=las[i]){
int d=dis[u]+(col[u]!=col[to[i]]);
if(d<dis[to[i]]){
dis[to[i]]=d;
if(!vis[to[i]]){
vis[to[i]]=1;
qu.push(to[i]);
}
}
}
}
REP(i,1,n*m)if(col[i])ret=max(ret,dis[i]+1);
return ret;
}
void init(){
read(n); read(m);
char s[maxn];
REP(i,1,n){
scanf("%s",s+1);
REP(j,1,m)a[i][j]=(s[j]=='B');
}
REP(i,1,n)REP(j,1,m)id[i][j]=(i-1)*m+j;
REP(i,1,n)REP(j,1,m){
col[id[i][j]]=a[i][j];
REP(k,1,4){
int x=i+dx[k],y=j+dy[k];
if(!judge(x,y))continue;
add(id[i][j],id[x][y]);
}
}
}
int main(){
File();
init();
REP(i,1,n*m)ans=min(ans,bfs(i));
printf("%d\n",ans);
return 0;
}