求最小覆盖边,
无向二分图的最小路径覆盖 = 顶点数 – 最大二分匹配数/2
由于构图过程中匹配双向的,所以匹配数多了一倍,最后要/2
构图可以用奇偶,或者用数来标记
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const long long mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
//const int M = 305;
struct node
{
int s,t,nxt ;
} e[5000] ;
int m,n,head[405],cnt,match[405],vis[405];
int find(int s)
{
for(int i=head[s] ; i!=-1 ; i=e[i].nxt)
{
int tt=e[i].t ;
if(!vis[tt])
{
vis[tt]=1 ;
if(match[tt]==-1 || find(match[tt]))
{
match[tt]=s ;
return 1 ;
}
}
}
return 0 ;
}
int max_match()
{
int ans=0 ;
memset(match,-1,sizeof(match)) ;
for(int i=1 ; i<=m ; i++)
{
memset(vis,0,sizeof(vis)) ;
ans+=find(i);
}
return ans;
}
void add(int s,int t)
{
e[cnt].s=s ;
e[cnt].t=t ;
e[cnt].nxt=head[s] ;
head[s]=cnt++ ;
}
//char M[405][405] ;
int mp[455][455] ;
int dx[]= {1,-1,0,0} ;
int dy[]= {0,0,1,-1} ;
int main()
{
int T ;
scanf("%d",&T) ;
while(T--)
{
int N,M;
scanf("%d%d",&N,&M) ;
n=0 ;
char tmp;
memset(mp,0,sizeof(mp)) ;
for(int i=0 ; i<N ; i++)
{
for(int j=0 ; j<M ; j++)
{
cin>>tmp;
if(tmp=='*')
{
mp[i][j]=++n;
}
//也可以用奇偶数建图
// if(tmp=='*')
// {
// if((i+j)&1)//奇点
// mp[i][j]=++ln;
// else//偶点
// mp[i][j]=++rn;
// }
// else
// mp[i][j]=0;
}
}
memset(head,-1,sizeof(head)) ;
cnt=0 ;
for(int i=0 ; i<N ; i++)
{
for(int j=0 ; j<M ; j++)
{
if(mp[i][j])
{
for(int k=0 ; k<4 ; k++)
{
int xx=i+dx[k] ;
int yy=j+dy[k] ;
if(xx<0 || xx>=N || yy<0 || yy>=M || !mp[xx][yy])
continue ;
if(mp[xx][yy])
{
add(mp[i][j],mp[xx][yy]) ;
}
}
}
}
}
m=n ;
printf("%d\n",n-max_match()/2) ;
}
return 0 ;
}