题意:给你一个50*50 的图,里面有LWC三种字符,L代表陆地,W代表水,C代表可以由你决定是水还是陆地,问你这个图中L的联通块最多有几块。
思路:首先很容易想到把所有L联通块周围围上W,剩下的就是C了,然后我们可以发现要使得L的联通块最多,那么就尽量每个联通块只占用给一个点,那么就变成了剩下的C连成的图,划分成两个点集,然后成为一个最小顶点覆盖问题,由于是二分图,所有转换成二分最大匹配,无向图的二分匹配随便搞搞。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100 + 5;
vector<int>G[2500 + 5];
int used[2500 + 5], match[2500 + 5];
bool dfs(int v)
{
used[v] = 1;
for(int i = 0; i < G[v].size(); i++)
{
int u = G[v][i], w = match[u];
if(w < 0 || !used[w] && dfs(w))
{
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int hungary(int V)
{
int res = 0;
memset(match, -1, sizeof(match));
for(int u = 1; u <= V; u++)
{
if(match[u] < 0)
{
memset(used, 0, sizeof(used));
if(dfs(u)) res++;
}
}
return res;
}
int n, m;
int dx[] = {0, 0, -1, 1};
int dy[] = {1, -1, 0, 0};
int vis[maxn][maxn];
char ma[maxn][maxn];
void dfs(int x, int y)
{
vis[x][y] = 1;
for(int i = 0; i < 4; i++)
{
int fx = dx[i] + x;
int fy = dy[i] + y;
if(0 <= fx && fx < n && 0 <= fy && fy < m)
{
if(ma[fx][fy] == 'C') ma[fx][fy] = 'W';
else if(ma[fx][fy] == 'L' && vis[fx][fy] == 0) dfs(fx, fy);
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0; i < n; i++)
cin >> ma[i];
int ans = 0;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(ma[i][j] == 'L' && vis[i][j] == 0)
{
dfs(i, j), ans++;
}
}
}
int id = 1;
map<pair<int,int>, int>rec;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(ma[i][j] == 'C')
{
if(rec[{i,j}] == 0) rec[{i,j}] = id++;
for(int k = 0; k < 4; k++)
{
int fx = i + dx[k], fy = j + dy[k];
if(0 <= fx && fx < n && 0 <= fy && fy < m)
{
if(ma[fx][fy] == 'C')
{
if(rec[{fx,fy}] == 0) rec[{fx, fy}] = id++;
int idx1 = rec[{i,j}], idx2 = rec[{fx, fy}];
G[idx1].push_back(idx2);
G[idx2].push_back(idx1);
}
}
}
}
}
}
int vs = id - 1;
if(vs) ans = ans + vs - hungary(vs);
cout << ans << endl;
return 0;
}