//用算法导论书上最大流做的二分图匹配,所以内存耗的比较大,因为需要队列来存储增广路径
//一个数组没有初始化,wrong了N次
//很多人都是用匈牙利算法做的,偶也准备学学传说中的匈牙利算法
#include<iostream>
using namespace std;
int h,w,flag,res,node;
int arr[500][500],map[50][20];
int q[10000],pre[10000],used[500];
int dx[]={-1,1,0,0};
int dy[]={0,0,-1,1};
int path(int s) //寻找增广路径
{ int u,head,tail,temp,i,j;
head=tail=0;
q[tail++]=s;
used[s]=1;
while(head<tail)
{ temp=tail;
for(i=head;i<temp;i++)
{ u=q[i];
if(u==1)
return 1;
for(j=0;j<node;j++)
if(used[j]==0 && arr[u][j]>0)
{pre[j]=u;
used[j]=1;
q[tail++]=j;
}
}
head=temp;
}
return 0;
}
void ford_fulkerson() //修改增光路径上边的残留容量,记录匹配的结果
{ int i,j,u,v,min,x,y;
min=INT_MAX;
u=pre[1];
v=1;
while(u>=0)
{if(arr[u][v]<min)
min=arr[u][v];
v=u;
u=pre[u];
}
u=pre[1];
v=1;
while(u>=0)
{ arr[u][v]=arr[u][v]-min;
arr[v][u]=arr[v][u]+min;
v=u;
u=pre[u];
}
res=res+min;
}
int main()
{int i,j,k,Case,x,y;
char c;
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&Case);
while(Case--)
{ flag=0;
res=0;
node=2;
scanf("%d%d",&h,&w);
memset(map,0,sizeof(map));
memset(used,0,sizeof(used));
memset(arr,0,sizeof(arr));
for(i=0;i<h;i++)
{getchar();
for(j=0;j<w;j++)
{ scanf("%c",&c);
if(c=='*')
map[i][j]=node++;
}
}
for(i=0;i<h;i++) //构图建模,抽象成二分图匹配的问题
for(j=0;j<w;j++)
{ if(map[i][j] && !used[map[i][j]])
{ arr[0][map[i][j]]=1;
used[map[i][j]]=1;
for(k=0;k<4;k++)
{ x=i+dx[k];
y=j+dy[k];
if(x>=0 && x<h && y>=0 && y<w && map[x][y])
{arr[map[i][j]][map[x][y]]=1;
arr[map[x][y]][1]=1;
used[map[x][y]]=1;
}
}
}
}
while(!flag)
{ memset(used,0,sizeof(used));
memset(pre,-1,sizeof(pre));
if(path(0))
ford_fulkerson();
else
flag=1;
}
printf("%d\n",node-2-res);
}
return 0;
}