原题: ZOJ 3781 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781
题意:
给一个n*m的X,O构成的格子,对一个点操作可以使与它相连通的所有一样颜色的格子翻转颜色(X->O或O->X),问给定的矩阵最少操作多少次可以全部变成一样的颜色。
网上思路:
每次操作都将本身所在的连通块与和自己相邻的不同颜色的连通块变成同一种颜色,也就是变成一个连通块了,那么要使n次操作后全部变成一样的颜色,也就是从某点出发到达其余所有点。所以先dfs把连通块缩成点,然后相邻的连通块之间建边,枚举以每个点为根的情况,bfs求出每种情况的深度,取最小的即为答案。
思路很重要,实现起来不难。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #define Mod 1000000007 using namespace std; #define N 44 char ss[N][N]; int ind[N][N],vis[N][N]; int now,n,m; int inq[1630]; int dx[4] = {1,0,-1,0}; int dy[4] = {0,1,0,-1}; vector<int> G[2001]; struct node { int dis,ind; }; int OK(int nx,int ny) { if(nx < n && nx >= 0 && ny < m && ny >= 0) return 1; return 0; } void dfs(int nx,int ny,int now) { for(int k=0;k<4;k++) { int kx = nx + dx[k]; int ky = ny + dy[k]; if(!OK(kx,ky)) continue; if(ss[kx][ky] == ss[nx][ny]) { if(ind[kx][ky] == -1) { ind[kx][ky] = now; dfs(kx,ky,now); } } else if(ind[kx][ky] != -1) //已经有标号,连边 { int v = ind[kx][ky]; G[v].push_back(now); G[now].push_back(v); } } } int SPFA(int num) { queue<node> que; memset(inq,0,sizeof(inq)); node S,tmp,now; S.dis = 0; S.ind = num; int res = 0; que.push(S); inq[num] = 1; while(!que.empty()) { tmp = que.front(); que.pop(); res = max(res,tmp.dis); now.dis = tmp.dis + 1; for(int i=0;i<G[tmp.ind].size();i++) { now.ind = G[tmp.ind][i]; if(!inq[now.ind]) { inq[now.ind] = 1; que.push(now); } } } return res; } int main() { int i,j,k; int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=0;i<n;i++) scanf("%s",ss[i]); now = 1; memset(ind,-1,sizeof(ind)); for(i=0;i<=n*m;i++) G[i].clear(); for(i=0;i<n;i++) for(j=0;j<m;j++) if(ind[i][j] == -1) { ind[i][j] = now; dfs(i,j,now); now++; } int ans = Mod; for(i=1;i<now;i++) ans = min(ans,SPFA(i)); printf("%d\n",ans); } return 0; }