ZOJ 3781 Paint the Grid Reloaded

题目链接~~>

做题感悟:比赛时最后一个多小时就不淡定了,看哪个哪个不会,处于不断切题状态,那时应该找一个做出来几率大的题思考。

题意:这题就两种颜色,颜色相同且有公共边的(或者通过另一个相同的颜色连接)为一个连通块,如果翻转其中一个,与它在同一个连通块里的都要改变颜色,求达到相同颜色的最小步数。

解题思路:dfs ( 建图) + bfs ( 寻找最优解 ) .

1)  dfs( 建图 ) ,因为翻转的时候每翻转连通块中一个整个连通块都翻转,这样你可以将其看成一个有边相连的无向图,每个边的两个顶点颜色都不一样。

              转化=======〉                

 

 

2)  bfs( 寻找最优解 ) , 建完图后就需要翻转计算最优解,可以枚举从每一点开始翻转所得到的最小步数,那怎样寻找最小步数 ? 假如从 v 这个顶点出发,那么与 v 相邻的顶点颜色必定都与 v 相反,so~> 你只需要把 v的颜色翻转,v与它相邻的所有顶点都是同一个颜色,这时可以把这些顶点看成一个连通块(顶点),再向四周扩展,再次扩展时,周围的颜色必定与此连通块颜色相反,再将它变成与周围顶点相同的颜色,这样又合并成为一个连通块……这样一直进行下去取最大步数。最后从最大步数中取出最小的步数即为最优解。

代码:

#include<stdio.h>
#include<iostream>
#include<map>
#include<stack>
#include<string>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std ;
const double PI = 3.1415926 ;
const double esp = 1e-4 ;
const int  md= 2810778 ;
const int INF = 999999999 ;
const int MX = 1625 ;
int n,m ;
char s[50][50] ;
int dx[5]={1,-1,0,0},dy[5]={0,0,1,-1} ;
int num[50][50] ;  // 用于标记属于哪个集合
bool vis[MX] ;
vector<int>G[MX] ;
struct node
{
    int x,step ;
} ;
int bfs(int x)
{
    int time=0 ;
    queue<node>q ;
    node curt,next ;
    memset(vis,false,sizeof(vis)) ;
    curt.x=x ;
    curt.step=0 ;
    vis[x]=true ;
    q.push(curt) ;
    while(!q.empty())
    {
        curt=q.front() ;
        q.pop() ;
        if(time<curt.step)
             time=curt.step ;
        int nx=G[curt.x].size() ;
        next.step=curt.step+1 ;
        for(int i=0 ;i<nx ;i++)
        {
            next.x=G[curt.x][i] ;
            if(!vis[next.x])
            {
                vis[next.x]=true ;
                q.push(next) ;
            }
        }
    }
    return  time ;
}
bool search(int x,int y)
{
    if(x<0||y<0||x>=n||y>=m)
         return false ;
         return true ;
}
void dfs(int x,int y,int ct,char ch)
{
    for(int i=0 ;i<4 ;i++)
    {
        int sx=x+dx[i] ;
        int sy=y+dy[i] ;
        if(search(sx,sy))
        {
            if(s[sx][sy]==ch)
            {
                if(num[sx][sy]==-1)
                {
                    num[sx][sy]=ct ;
                    dfs(sx,sy,ct,ch) ;
                }
            }
            else if(num[sx][sy]!=-1)
            {
                int temp=num[sx][sy] ;
                G[ct].push_back(temp) ; // 建双向边
                G[temp].push_back(ct) ;
            }
        }
    }
}
void init() // 初始化
{
    memset(num,-1,sizeof(num)) ;
    for(int i=0 ;i<n*m ;i++)
         G[i].clear() ;
}
int main()
{
    int Tx,cnt ;
    scanf("%d",&Tx) ;
    while(Tx--)
    {
        scanf("%d%d",&n,&m) ;
        init() ;
        cnt=0 ;
        for(int i=0 ;i<n ;i++)
          scanf("%s",s[i]) ;
        for(int i=0 ;i<n ;i++)
          for(int j=0 ;j<m ;j++)
           if(num[i][j]==-1) // 如果没访问过
           {
               num[i][j]=cnt++ ;
               dfs(i,j,cnt-1,s[i][j]) ;
           }
        int ans=INF ;
        for(int i=0 ;i<cnt ;i++) // 广搜寻找最优解
        {
            int mx=bfs(i) ;
            ans=ans > mx ? mx : ans ;
        }
        printf("%d\n",ans) ;
    }
    return 0 ;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值