题目具体讲什么没怎么看,本质就是说给一个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; }