ZOJ-3781-Paint the Grid Reloaded【11th浙江省赛】【连通块缩点】【spfa】【好题】

ZOJ-3781-Paint the Grid Reloaded

                    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 <= N, M <= 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

题目链接:ZOJ 3781

题目大意:字符一样并且相邻的即为连通。每次可翻转一个连通块X(O)的颜色,问至少改变几次使得图上所有字符都相等

题目思路:每次操作都将本身所在的连通块与和自己相邻的不同颜色的连通块变成同一种颜色,也就是变成一个连通块了,那么要使n次操作后全部变成一样的颜色,也就是从某点出发到达其余所有点。

所以,dfs把连通块缩成点,然后相邻的连通块之间建边,枚举以每个点为根的情况,用spfa求最短路。

注意:去除重边,a->b 这种边重复加了很多次导致 SPFA 做了很多无意义的计算,会导致超时

以下是代码:

#include <bits/stdc++.h>
#define mst(a) memset(a,0,sizeof (a))
#define INF 1e9 
#define FOR(i,n) for (int i = 0; i < n; i++)
#define eps 1e-10
using namespace std;

typedef long long ll;
struct node
{
    int v,len;  //终点、长度 
    node(int a,int b){
        v = a,len = b;
    }
};


vector <node> tree[2222];
int d[2222];  //用于记录x点到各点的距离 

string s[110];
int vis[110][110];
int n,m;

int g[110][110];
int cur;
bool check(int a,int b)   //检查重边,重边太多会有很多无意义的计算,导致超时 
{
    for (int i = 0; i < tree[a].size(); i++)
    {
        if (tree[a][i].v == b) return 0;
    }
    return 1;
}
void dfs(int row,int col,char type)
{
    if (vis[row][col])
    {
        if (s[row][col] != type && (row > 0 && row <= n && col > 0 && col <= m))
        {
            if (g[row][col] && check(g[row][col],cur))   //如果下一个和这个不一样就建立一条边 
            {
                node tmp(g[row][col],1);  //起点、长度 
                tree[cur].push_back(tmp);               
                tmp.v = cur;
                tree[g[row][col]].push_back(tmp);
            } 
        }
        return;
    }
    if (s[row][col] == type)
    {
        vis[row][col] = 1;
        g[row][col] = cur;
        if (row > 1)dfs(row - 1,col,type);
        if (row < n)dfs(row + 1,col,type);
        if (col > 1)dfs(row,col - 1,type);
        if (col < m)dfs(row,col + 1,type);
    }
}

void SPFA(int x)  //x为起点
{
    //tree为邻接链表 
    int v[2222];
    memset(v,0,sizeof(v));
    queue<int>q;
    q.push(x);
    v[x] = 1;d[x] = 0;
    while(!q.empty())
    {
        int nod = q.front();
        q.pop();
        v[nod] = 0;
        for(int i = 0;i < tree[nod].size();i++)
        {
            int nxtnod = tree[nod][i].v;
            if(d[nxtnod] > d[nod] + tree[nod][i].len)
            {
                d[nxtnod] = d[nod] + tree[nod][i].len;
                if(!v[nxtnod])
                {
                    v[nxtnod] = 1;
                    q.push(nxtnod);
                }
            }
        }
    }
}
int main(){
    int t;
    cin >> t;
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        for (int i = 0; i < 50; i++) s[i].clear();
        for (int i = 0; i < 2000; i++) tree[i].clear();
        cur = 1;
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> s[i],s[i] = " " + s[i];
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (!vis[i][j])
                {
                    dfs(i,j,s[i][j]);
                    cur++;
                }
            }
        }
        int ans = INF; 
        for (int i = 1; i < cur; i++)  //遍历起点 
        {
            memset(d,1,sizeof(d));  //初始化为INF
            SPFA(i);
            int ret = 0;
            for (int j = 1; j < cur; j++)
            {
                ret = max(d[j],ret);
            }
            ans = min(ret,ans);
        }
        cout << ans << endl;
    } 
    return 0;
}

你可能感兴趣的:(ZOJ,3781)