zoukankan      html  css  js  c++  java
  • ZOJ 3781

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781

    Time Limit: 2 Seconds      Memory Limit: 65536 KB

    Leo has a grid with N rows and M columns. All cells are painted with either black or white initially.

    Two cells A and B are called connected if they share an edge and they are in the same color, or there exists a cell C connected to both A and B.

    Leo wants to paint the grid with the same color. He can make it done in multiple steps. At each step Leo can choose a cell and flip the color (from black to white or from white to black) of all cells connected to it. Leo wants to know the minimum number of steps he needs to make all cells in the same color.

    Input

    There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

    The first line contains two integers N and M (1 <= NM <= 40). Then N lines follow. Each line contains a string with N characters. Each character is either 'X' (black) or 'O' (white) indicates the initial color of the cells.

    Output

    For each test case, output the minimum steps needed to make all cells in the same color.

    Sample Input

    2
    2 2
    OX
    OX
    3 3
    XOX
    OXO
    XOX
    

    Sample Output

    1
    2
    

    Hint

    For the second sample, one optimal solution is:

    Step 1. flip (2, 2)

    XOX
    OOO
    XOX
    

    Step 2. flip (1, 2)

    XXX
    XXX
    XXX

    题意:

    两个方格字符一样并且相邻的即为判断为连通,且连通具有传递性;

    每次翻转,可以也必须翻转一个连通块的颜色(X→O,O→X),问至少翻转几次可以使得给出的图变得所有方格颜色都一样。

    题解:

    若把所有连通块缩成一个点看待,那么整个n*m的grid可以变成一个无向二分图;

    那么,我们在这个二分图上任取一个点作为出发点,假设这个点在集合L,它沿着一条边走到另一个集合R内的另一个点;

    这种沿着一条边走一步的动作,可以看做起点代表的那个连通块翻转了颜色,变成了与终点代表的连通块一样的颜色;

    更形象的,相当于该条边的起点并入了终点;

    那么我们一直走,就相当于不断地翻转颜色,直到遍历完全部二分图上所有点,就相当于把整个grid都翻成了一个颜色。

    那么,翻转次数相当于什么呢?显然就是走过的边数。

    显然,我们如果规定好起点,令其深度d[st]=0,那么bfs不断一层层地求其相邻点的d[],

    直到全部搜索完,所有d[i]中的最大值,就是以st为起点需要翻转几次才能颜色全部一样。

    另外,枚举起点为从(1,1)到(n,m)进行一次dfs就能把所有连通块缩成点,并且建立起一个二分图,这个正确性是可以想见的。

    同时,存图方面,本题会卡邻接矩阵,需要使用邻接表。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    const int maxn=43;
    const int maxm=43;
    
    int n,m;
    char grid[maxn][maxm]; //存储grid
    int id[maxn][maxm];
    
    struct Edge{
        int u,v;
        Edge(int u,int v){this->u=u,this->v=v;}
    };
    vector<Edge> E;
    vector<int> G[maxn*maxm];
    void init(int n)
    {
        E.clear();
        for(int i=0;i<=n;i++) G[i].clear();
    }
    void addedge(int u,int v)
    {
        E.push_back(Edge(u,v));
        E.push_back(Edge(v,u));
        int _size=E.size();
        G[u].push_back(_size-2);
        G[v].push_back(_size-1);
    }
    
    int dr[4]={0,1,0,-1};
    int dc[4]={1,0,-1,0};
    
    void dfs(int nowr,int nowc,int i)
    {
        id[nowr][nowc]=i;
        for(int k=0;k<4;k++)
        {
            int nxtr=nowr+dr[k];
            int nxtc=nowc+dc[k];
    
            if(!(1<=nxtr && nxtr<=n && 1<=nxtc && nxtc<=m)) continue;
            if(grid[nowr][nowc]==grid[nxtr][nxtc])
            {
                if(id[nxtr][nxtc]==0) dfs(nxtr,nxtc,i);
            }
            else
            {
                if(id[nxtr][nxtc]!=0)
                {
                    int idnow=id[nowr][nowc], idnxt=id[nxtr][nxtc];
                    addedge(idnow,idnxt);
                }
            }
        }
    }
    
    int d[maxn*maxm];
    bool vis[maxn*maxm];
    int bfs(int st,int cnt)
    {
        memset(vis,0,sizeof(vis));
    
        int res=0;
        queue<int> q;
        q.push(st);
        vis[st]=1;
        d[st]=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            res=max(d[u],res);
            for(int i=0,_size=G[u].size();i<_size;i++)
            {
                int v=E[G[u][i]].v;
                if(vis[v]) continue;
                d[v]=d[u]+1;
                q.push(v);
                vis[v]=1;
            }
        }
    
        return res;
    }
    
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++) scanf("%s",grid[i]+1);
    
            //连通块编号 - O(n*m)
            init(m*n);
            memset(id,0,sizeof(id));
            int cnt=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    if(id[i][j]!=0) continue;
                    dfs(i,j,++cnt);
                }
            }
    
            //for(int i=1;i<=cnt;i++) {for(int j=1;j<=cnt;j++) printf("%d ",edge[i][j]); printf("
    ");}
    
            //SPFA求单源最短路 - O((n*m)^2)
            int ans=INF;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)
                {
                    ans=min(bfs(id[i][j],cnt),ans);
                }
            }
    
            printf("%d
    ",ans);
        }
    }
  • 相关阅读:
    基础知识漫谈(5):应用面向对象来分析“语言”
    【线段树】BZOJ2752: [HAOI2012]高速公路(road)
    【树状数组】BZOJ3132 上帝造题的七分钟
    【AC自动机】Lougu P3796
    【Splay】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)
    【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)
    【可持久化线段树】POJ2104 查询区间第k小值
    【RMQ】洛谷P3379 RMQ求LCA
    【倍增】洛谷P3379 倍增求LCA
    【网络流】POJ1273 Drainage Ditches
  • 原文地址:https://www.cnblogs.com/dilthey/p/8922257.html
Copyright © 2011-2022 走看看