ZOJ 3781 Paint the Grid Reloaded(DFS连通块缩点+BFS求最短路)

题目链接: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 }

猜你喜欢

转载自www.cnblogs.com/fu3638/p/9056004.html