题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5268
题目大意:字符一样并且相邻的即为连通。每次可翻转一个连通块X(O)的颜色,问至少改变几次使得图上所有字符都相等。
解题思路:
1) dfs( 建图 ) ,因为翻转的时候每翻转连通块中一个整个连通块都翻转,
这样你可以将其看成一个有边相连的无向图,每个边的两个顶点颜色都不一样。
2) bfs( 寻找最优解 ) , 建完图后就需要翻转计算最优解,可以枚举从每一点开始翻转所得到的最小步数,那怎样寻找最小步数 ?
假如从 v 这个顶点出发,那么与 v 相邻的顶点颜色必定都与 v 相反,so~> 你只需要把 v的颜色翻转,
v与它相邻的所有顶点都是同一个颜色,这时可以把这些顶点看成一个连通块(顶点),再向四周扩展,
再次扩展时,周围的颜色必定与此连通块颜色相反,再将它变成与周围顶点相同的颜色,
这样又合并成为一个连通块……这样一直进行下去取最大步数。最后从最大步数中取出最小的步数即为最优解。
代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<cctype> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 #include<vector> 8 #include<queue> 9 #include<set> 10 #include<map> 11 #include<stack> 12 #include<string> 13 #define lc(a) (a<<1) 14 #define rc(a) (a<<1|1) 15 #define MID(a,b) ((a+b)>>1) 16 #define fin(name) freopen(name,"r",stdin) 17 #define fout(name) freopen(name,"w",stdout) 18 #define clr(arr,val) memset(arr,val,sizeof(arr)) 19 #define _for(i,start,end) for(int i=start;i<=end;i++) 20 #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); 21 using namespace std; 22 typedef long long LL; 23 const int N=50+5; 24 const int INF=0x3f3f3f3f; 25 const double eps=1e-10; 26 27 int n,m,cnt; 28 int path[N*N],vis[N][N]; 29 int d[4][2]={0,1,1,0,0,-1,-1,0}; 30 char mp[N][N]; 31 vector<int>v[N*N]; 32 33 bool judge(int x,int y){ 34 if(x>=1&&y>=1&&x<=n&&y<=m&&!vis[x][y]) 35 return true; 36 return false; 37 } 38 39 void dfs(int x,int y){ 40 vis[x][y]=cnt; 41 for(int i=0;i<4;i++){ 42 int xx=x+d[i][0]; 43 int yy=y+d[i][1]; 44 if(judge(xx,yy)&&mp[x][y]==mp[xx][yy]){ 45 dfs(xx,yy); 46 } 47 } 48 } 49 50 int bfs(int st){ 51 memset(path,-1,sizeof(path)); 52 queue<int>q; 53 q.push(st); 54 path[st]=0; 55 while(!q.empty()){ 56 int k=q.front(); 57 q.pop(); 58 for(int i=0;i<v[k].size();i++){ 59 int t=v[k][i]; 60 if(path[t]==-1){ 61 path[t]=path[k]+1; 62 q.push(t); 63 } 64 } 65 } 66 } 67 68 int main(){ 69 FAST_IO; 70 int t; 71 cin>>t; 72 while(t--){ 73 memset(vis,0,sizeof(vis)); 74 cin>>n>>m; 75 for(int i=1;i<=n*m;i++) v[i].clear(); 76 for(int i=1;i<=n;i++){ 77 cin>>mp[i]+1; 78 } 79 cnt=0; 80 //找连通块,缩点 81 for(int i=1;i<=n;i++){ 82 for(int j=1;j<=m;j++){ 83 if(!vis[i][j]){ 84 ++cnt; 85 dfs(i,j); 86 } 87 } 88 } 89 //连通块间建边 90 for(int i=1;i<=n;i++){ 91 for(int j=1;j<=m;j++){ 92 for(int k=0;k<4;k++){ 93 int xx=i+d[k][0]; 94 int yy=j+d[k][1]; 95 if(vis[xx][yy]!=vis[i][j]){ 96 int t1=vis[i][j],t2=vis[xx][yy]; 97 v[t1].push_back(t2); 98 } 99 } 100 } 101 } 102 //枚举每个连通块为起点,每次找出最大路径,找出这些最大路径中的最小值 103 int ans=INF; 104 for(int i=1;i<=cnt;i++){ 105 bfs(i); 106 int mmax=-INF; 107 for(int j=1;j<=cnt;j++){ 108 mmax=max(mmax,path[j]); 109 } 110 ans=min(ans,mmax); 111 } 112 cout<<ans<<endl; 113 } 114 return 0; 115 }