题目链接,请点击这里
要使用的定理
关于二分图常用定理
1.最大匹配数 = 最小覆盖数(Konig定理)
2.二分图最小边覆盖 = 顶点数 - 最大匹配数
3.无向图最小边覆盖 = 无向图的最小边覆盖 = (二分图两边顶点数 - 二分图的最大匹配数/2)
至于为什么/2
请看博客https://blog.csdn.net/csyifanZhang/article/details/107081503
思路
先把孤立点城市连城线,变成图,对于图中每一个连通分量,只少用多少边才能将所
有点覆盖,这就是比较典型的无向图最小边覆盖问题
(最小点覆盖 = 最大匹配数 -> 匈牙利算法)
AC代码块
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 50;
int g[N][N],col, row,n, match[N*N], ans;
int dx[] = {0,0,1,-1}, dy[] = {1,-1,0,0};
bool vis[N*N];
string s[N];
vector<int>G[N*N];//建图的城市
void build(int x, int y) //建图把孤立点城市连成线
{
for(int i = 0; i< 4; i++)
{
int xx = x + dx[i], yy = y + dy[i];
if(xx < 0 || xx > row || yy < 0 || yy > col || s[xx][yy] == 'o') continue;
G[g[x][y]].push_back(g[xx][yy]);
}
}
//无向图的最小边覆盖 = (二分图两边顶点数 - 二分图的最大匹配数/2)
bool find(int u) //把每个城市两两的匹配在一起,从而可以球的最大匹配数量
{
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(!vis[v])
{
vis[v] = true;
if(!match[v] || find(match[v])) //没匹配过或者匹配过但是能为其找到下家
{
match[v] = u;
return true;
}
}
}
return false;
}
int main()
{
cin >> n;
while(n--)
{
cin >> row >> col;
memset(g,0,sizeof(g));
for(int i = 0; i < row; i++ )
{
cin >> s[i];
}
//给城市编号
ans = 1;
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
if(s[i][j] == '*') //城市
{
g[i][j] = ans++;
}
}
}
//建图
for(int i = 0; i < N*N; i++) G[i].clear();
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
if(s[i][j] == '*')
{
build(i,j);//把城市连成线
}
}
}
//匹配
int cnt = 0;
memset(match, 0, sizeof(match));
for(int i = 1; i < ans; i++)
{
memset(vis, 0,sizeof(vis));
if(find(i)) cnt++;
}
printf("%d\n",ans - 1 - cnt/2);
}
return 0;
}