题目具体讲什么没怎么看,本质就是说给一个n*m的图,里面有*和o,现在让你用1*2或2*1的矩阵覆盖图中的*,求最少需要多少个。
建图:对于相邻的两个*,连一条边,然后将一个*,拆做两个,分别属于X、Y集合,得到一个二分图。对于该二分图,边即代表用来覆盖的矩阵,问题转化为用最少的边覆盖所有的点。对于已经匹配的点,用匹配的边去覆盖就好了。剩下的点个数=顶点总数-最大匹配数*2.各自需要一个来覆盖,因此总共需要的最少的边数为顶点总数-最大匹配数
注意到,这里的二分图是拆点后形成的一个无向图(边是对称出现的),因此匹配的时候,同一个点会被匹配两次。因此实际上的最大匹配数为所求得的匹配数的一半。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<stack>
#include<vector>
#include<cmath>
using namespace std;
#define N 50
bool Map[550][550],T[550];
int Left[550],M[50][50],mov[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
int match(int u,int n){
for(int v=1;v<=n;++v){
if(Map[u][v]&&!T[v]){
T[v]=1;
if(!Left[v]||match(Left[v],n)){
Left[v]=u;
return 1;
}
}
}
return 0;
}
int main()
{
int t,cnt,i,j,n,m,k;
cin>>t;
while(t--){
cnt=0;
scanf("%d%d",&n,&m);
getchar();
memset(M,0,sizeof(M));
memset(Map,0,sizeof(Map));
for(i=0;i<n;++i)
for(j=0;j<=m;++j) if(getchar()=='*') M[i][j]=++cnt;
for(i=0;i<n;++i)
for(j=0;j<m;++j)
if(M[i][j]){
for(k=0;k<4;++k){
int ii=i,jj=j;
ii+=mov[k][0];
jj+=mov[k][1];
if(ii>=0&&jj>=0&&ii<n&&jj<m&&M[ii][jj]) Map[M[i][j]][M[ii][jj]]=1;
}
}
int ans=0;
memset(Left,0,sizeof(Left));
for(i=1;i<=cnt;++i){
for(j=1;j<=cnt;++j) T[j]=0;
ans+=match(i,cnt);
}
printf("%d\n",cnt-ans/2);
}
return 0;
}