Codeforces 37E Trial for Chief 题解 [ 蓝 ] [ 图论建模 ] [ 01 BFS ] [ 倒序操作 ] [ 结论 ]
Trial for Chief:不错的图论结论题。
注意到正着考虑不好想,因此考虑倒序操作,将终态设为起点,初态设为终点。
不难发现倒序操作后只有对某个极大同色连通块操作才是最优的,并且如果某个连通块不同色就无法进行操作。因为如果不选极大同色连通块,则选的一定是一个子连通块,一定被某个更大的连通块所包含,在大连通块被操作之后就不会让小连通块一起改变,花费一定更多。
因此原问题被转化为:有黑白两种颜色,每次你能反转一个极大同色连通块的颜色,求全部变成白色的最小操作次数。
发现反转的过程很像 BFS 逐层拓展的过程,如果一直选一个点所在的连通块反转,那么就相当于以该连通块为起点扩展一层 BFS,则最终需要的反转次数就是从该连通块到其余连通块的最大距离。
可以证明选多个点为反转中心一定是不优的,因为最终某个子图操作下界就是子图直径的一半(上取整),多个子图拼起来并不会让最终方案更优,反而会让步数更多。
因此用 BFS 求出所有连通块,枚举每个点为起点跑 BFS 取最远距离即可;或者用 01 BFS 来实现也是可以的。注意如果最后变成的是黑色,要多花一步变成白色,这个可以根据最远距离的奇偶性判断。
时间复杂度 \(O(n^2m^2)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=55,inf=0x3f3f3f3f;
int gox[]={0,0,1,-1};
int goy[]={1,-1,0,0};
int n,m,dis[N][N],ans=inf;
bitset<N>a[N],vis[N];
int h,t;
pi q[100005];
bool legal(int x,int y)
{
return (1<=x&&x<=n&&1<=y&&y<=m);
}
void solve(int sx,int sy)
{
int res=0;
memset(dis,0x3f,sizeof(dis));
dis[sx][sy]=0;
h=t=50000;
q[t++]={sx,sy};
for(int i=1;i<=n;i++)vis[i].reset();
while(t-h)
{
pi u=q[h++];
int x=u.fi,y=u.se;
if(vis[x][y])continue;
vis[x][y]=1;
for(int i=0;i<4;i++)
{
int tx=x+gox[i],ty=y+goy[i],w=(a[x][y]^a[tx][ty]);
if(legal(tx,ty)&&dis[tx][ty]>dis[x][y]+w)
{
dis[tx][ty]=dis[x][y]+w;
if(w==0)q[--h]={tx,ty};
else q[t++]={tx,ty};
}
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
res=max(res,dis[i][j]);
ans=min(ans,(((res&1)^(a[sx][sy]&1))==0?res:res+1));
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
char c;
cin>>c;
a[i][j]=(c=='W'?0:1);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
solve(i,j);
cout<<ans;
return 0;
}

浙公网安备 33010602011771号