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;
}
posted @ 2025-05-25 19:09  KS_Fszha  阅读(11)  评论(0)    收藏  举报