/*
    40*10的地图,有一些'*'必须用方块覆盖到
    而每块方块只能覆盖长度为1*2
    问最少需要的方块数

    状态压缩dp,主要就是枚举当前行的状态to,上一行的状态from,由from的值去更新to的值

    骨牌覆盖类求方案数的,一般都需要枚举所有情况,即放、不放
    但这种求最优值的,我一开始枚举所有情况,导致超时!!
    需要人肉优化
    比如,这里空的位置肯定不用放东西,直接下一列

    这道题,转移时只需考虑当前row和上一行row-1,
    对于列col 分4种情况,
    00 01 10 11
    1表示该位置是'*'
    分这4种情况:
    ①不放 状态构造为 to<<1 , from<<1
    ②上一行有'*'  状态构造为 to<<1 , from<<1|1
    ③当前行有'*'  由于上一行没有,就没必要竖放了,直接横放了,竖放不会导致最优解
       然后还要考察col+1是否有'*',有的话状态要加上去
       当然,该位置可以不放,为了给下一行覆盖
    ④上下行都有'*'
       不放的话,上一行就需要有覆盖了
       横放,上一行肯定就要被覆盖了
       竖放,上一行可选择被覆盖,或者没被覆盖
       但选择被覆盖是不会导致最优解的,因为横放的时候就会考虑上一行该位置要被覆盖了
        

    求最优值(不是求方案),没必要转移时 from -> to 把一些明显不优的状态from转移到to去
    如对于'O'位置,可以放,但肯定不去放它,放了不会更优的,所以不进入这个分支了
    分支数决定了规模,人肉优化,不进入没必要的分支
    但求方案数目的话,就要考虑所有情况了,放、不放之类的
*/
    
#include
<iostream>
#include
<cstring>
#include
<map>
#include
<algorithm>
#include
<stack>
#include
<queue>
#include
<cmath>
#include
<string>
#include
<cstdlib>
#include
<vector>
#include
<cstdio>
#include
<set>
#include
<list>
#include
<numeric>
#include
<cassert>

using namespace std;

const int INF = 1000000000;

char field[50][20];
int dp[50][1<<10];
int H, W;

void dfs(int row, int col , int s1 , int s2 , int num){//当前检查col
    /*if(col > W){
        return;
    }
*/

    
if(col >=  W){//因为可能在最后一列水平覆盖,所以出格了
        if(col>W){//注意>>
            s1 >>= 1;
            s2 
>>= 1;
        }

        dp[row][s1] 
= min(dp[row][s1] , dp[row-1][s2] + num);
        
return;
    }

    
int code = (field[row][col] == '*')*2 + (field[row-1][col] == '*');
    
    
switch(code){
        
case 0:
            dfs(row, col
+1, s1<<1, s2<<1, num);            
            
break;
        
case 1:
            dfs(row, col
+1, s1<<1, s2<<1|1, num);//用|去覆盖(row-1,col)跟(row-1,col)自己覆盖效果一样
            break;
        
case 2:
            dfs(row, col
+2, (s1<<2)|2|(field[row][col+1== '*'), (s2<<2)|(field[row-1][col+1== '*'), num+1);
            
//nothing
            dfs(row, col+1, s1<<1, s2<<1, num);
            
break;
        
case 3:
            
//nothing
            dfs(row, col+1, s1<<1, s2<<1|1, num);
            dfs(row, col
+1, s1<<1|1, s2<<1, num+1);
            
//dfs(row, col+1, s1<<1|1, s2<<1|1, num+1);  s2<<1|1在下面的状态会考虑到的,而且下面的不会比它差 所以不用再dfs
            dfs(row, col+2, (s1<<2)|2|(field[row][col+1== '*'), (s2<<2)|2|(field[row-1][col+1== '*'), num+1);
            
break;
    }

}


int main()
{
#ifndef ONLINE_JUDGE
    freopen(
"in","r",stdin);
#endif

    
int T;
    
for(scanf("%d",&T); T-- ;){
        scanf(
"%d%d",&H,&W);
        
for(int i = 1 ; i <= H; i++){
            scanf(
"%s",field[i]);
        }
    
        
for(int row = 1 ; row <= H ; row++){
            fill(dp[row] , dp[row] 
+ (1<<W), INF);
            dfs(row,
0,0,0,0);
        }

        
int ans = INF;
        
int mask = 0;
        
for(int i = 0; i < W ; i++){
            mask 
<<= 1;
            mask 
|= (field[H][i] == '*');
        }

        
for(; mask < (1<<W);mask++){
            ans 
= min(ans, dp[H][mask]);
        }

        printf(
"%d\n",ans);
    }
    
    
return 0;
}