将每一个*编号,然后建二分图,注意边是双向的。
于是,问题就转化为求最小路径覆盖。
由公式:最小路径 = 顶点的数量 - 最大匹配 。
有向无环图的最小路径覆盖(数)=二分图的最小边覆盖集(数)=二分图的最大独立集(数)=全集-二分图的最小点覆盖集(数)=全集-二分图的最大匹配数
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);
}
}