二分圖最小路徑覆蓋=|v|-最大匹配。此題爲有向圖,切所有邊正反向存了兩遍,所以結果匹配數要除以2
//
// main.cpp
// poj3020
//
// Created by Fangpin on 15/5/29.
// Copyright (c) 2015年 FangPin. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN=5000;
int link[MAXN],n,m,total,h[50][20];
vector<int> g[MAXN];
char mp[55][22];
bool vis[MAXN];
bool dfs(int x){
for(int i=0;i<g[x].size();++i){
int y=g[x][i];
if(vis[y]) continue;
vis[y]=true;
if(link[y]==-1 || dfs(link[y])){
link[y]=x;
return true;
}
}
return false;
}
int hungry(){
memset(link,-1,sizeof(link));
int ans=0;
for(int i=1;i<=total;++i){
memset(vis,false,sizeof(vis));
if(dfs(i)) ++ans;
}
return ans;
}
int main(int argc, const char * argv[]) {
// insert code here...
// std::cout << "Hello, World!\n";
int t;
scanf("%d",&t);
char s[10000];
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=n*m;++i) g[i].clear();
memset(mp,'o',sizeof(mp));
total=0;
for(int i=0;i<n;++i){
scanf("%s",s);
int len=strlen(s);
for(int j=0;j<len;++j){
mp[i+1][j+1]=s[j];
if(s[j]=='*') h[i+1][j+1]=++total;
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(mp[i][j]=='*'){
int x=h[i][j];
if(mp[i-1][j]=='*') g[x].push_back(h[i-1][j]);
if(mp[i+1][j]=='*') g[x].push_back(h[i+1][j]);
if(mp[i][j-1]=='*') g[x].push_back(h[i][j-1]);
if(mp[i][j+1]=='*') g[x].push_back(h[i][j+1]);
}
}
}
cout<<total-hungry()/2<<endl;
}
return 0;
}