将每一个*编号,然后建二分图,注意边是双向的。
于是,问题就转化为求最小路径覆盖。
由公式:最小路径 = 顶点的数量 - 最大匹配 。
有向无环图的最小路径覆盖(数)=二分图的最小边覆盖集(数)=二分图的最大独立集(数)=全集-二分图的最小点覆盖集(数)=全集-二分图的最大匹配数
PS:此题的边是双向的,所以匹配数会多一倍,于是要ans/2才是真正的匹配数。
单独的顶点是一条路径
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; int map[500][500]; int p[510],f; bool v[510]; bool find(int d) { int i; for (i=1; i<=f; i++) { if (map[d][i] == 1 && v[i] == false) { v[i]=true; if (p[i] == -1 || find(p[i]) == true) { p[i]=d; return true; } } } return false; } int main() { int T,i,j,ans,r,c,h,w,nt[50][20]; char t[50][20]; scanf("%d",&T); while (T--) { scanf("%d%d",&h,&w); getchar(); f=1; for (i=0; i<h; i++) { scanf("%s",t[i]); for (j=0; j<w; j++) { if (t[i][j] == '*') { nt[i][j]=f; f++; } else { nt[i][j]=0; } } } memset(map,0,sizeof(map)); memset(p,-1,sizeof(p)); for (i=0; i<h; i++) { for (j=0; j<w; j++) { if (nt[i][j] != 0) { if (i-1 >= 0 && nt[i-1][j] != 0) { map[nt[i][j]][nt[i-1][j]]=1; map[nt[i-1][j]][nt[i][j]]=1; } if (j-1 >= 0 && nt[i][j-1] != 0) { map[nt[i][j]][nt[i][j-1]]=1; map[nt[i][j-1]][nt[i][j]]=1; } if (i+1 < h && nt[i+1][j] != 0) { map[nt[i][j]][nt[i+1][j]]=1; map[nt[i+1][j]][nt[i][j]]=1; } if (j+1 < w && nt[i][j+1] != 0) { map[nt[i][j+1]][nt[i][j]]=1; map[nt[i][j]][nt[i][j+1]]=1; } } } } ans=0; for (i=1; i<=f; i++) { memset(v,false,sizeof(v)); if (find(i)) ans++; } printf("%d\n",f-1-ans/2); } }