ZOJ 3781 - Paint the Grid Reloaded - [DFS连通块缩点建图+BFS求深度][第11届浙江省赛F题]

题目链接: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("\n");}

        //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\n",ans);
    }
}
posted @ 2018-04-23 22:18  Dilthey  阅读(312)  评论(0编辑  收藏  举报