连通块缩点应该是比较好的方法,只跑150ms。
这个方法比较笨,跑700ms。大概思路是:
每个连通块的找一个点,判断这个点周围有几层包围圈。
用队列q[i]表示第i层包围圈的元素都有哪些。
#include<iostream>
#include<cstdio>
#include<queue>
#include<string.h>
using namespace std;
const int N=42;
char mp[N][N];
int mem[N][N],visit[N][N];
int dir[4][2]={1,0,-1,0,0,1,0,-1};
int tt,n,m;
struct Point{
int x,y;
Point(int _x,int _y){
x=_x;y=_y;
}
};
queue<Point> q[2*N];
void bfs(int k){
while(!q[k].empty()){
Point t=q[k].front();q[k].pop();
if(visit[t.x][t.y]==1) continue;
visit[t.x][t.y]=1;
for(int i=0;i<4;i++){
int nx=t.x+dir[i][0],ny=t.y+dir[i][1];
if(nx<1||ny<1||nx>n||ny>m) continue;
if(!visit[nx][ny]){
if(mp[nx][ny]==mp[t.x][t.y]){
q[k].push(Point(nx,ny));
}
else{
q[k+1].push(Point(nx,ny));
}
}
}
}
}
void solve(int x,int y,int &ans){
q[0].push(Point(x,y));
for(int i=0;i<=n+m&&i<=ans;i++){
if(q[i].empty()){
ans=min(ans,i-1);
return;
}
bfs(i);
}
}
void dfs(int x,int y){
for(int i=0;i<4;i++){
int nx=x+dir[i][0],ny=y+dir[i][1];
if(nx<1||ny<1||nx>n||ny>m) continue;
if(mp[x][y]!=mp[nx][ny]) continue;
if(!mem[nx][ny]){
mem[nx][ny]=1;
dfs(nx,ny);
}
}
}
void init(){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
visit[i][j]=0;
}
for(int i=0;i<=max(n,m);i++){
while(!q[i].empty())
q[i].pop();
}
}
int main(){
cin>>tt;
while(tt--){
cin>>n>>m;
if(n>40||m>40) return 1;
for(int i=1;i<=n;i++)
scanf("%s",mp[i]+1);
int ans=100;
memset(mem,0,sizeof mem);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mem[i][j]) continue;
mem[i][j]=1;
dfs(i,j);
init();
solve(i,j,ans);
}
}
printf("%d\n",ans);
}
return 0;
}