///////////////////////////////////////////////
//最大堡垒数
//用回溯法(其实是N皇后问题的改装)+暴力A掉
#include<iostream>
#include<math.h>
using namespace std;
char a[5][5];
//堡垒类,方便对成员的访问
class Blockhouses
{
friend int nBlock(int);
private:
bool Place(int k);
bool Place2(int k);
void Backtrack(int t,int lever);
int n,
**x;
int max;
};
//判断该位置能否放置堡垒
bool Blockhouses::Place(int k)
{
int i,j;
bool flags=1;
if(k==-1 || k==0) return 1; //由于在下面的调用中,k有可能小于0
for(i=1;i<k;i++)
{
//当列数相等时,判断x[i]与x[k]之间是否有墙
if(x[i][2]==x[k][2])
{
flags=0;
for(j=x[i][1]+1; j<x[k][1]; j++)
{
if(a[j][ x[i][2] ]=='X')
flags=1;
}
if(!flags) return 0;
}
}
return flags;
}
//由于行数没有判断,所以加多一个判行数的
bool Blockhouses::Place2(int k)
{
int j;
//不同行的马上可以返回1
if(x[k-1][1]!=x[k][1]) return 1;
//剩下的是行数相同的,判其中间是否有墙
for(j=x[k-1][2]+1; j<x[k][2]; j++)
if(a[ x[k][1] ][j]=='X')
return 1;
return 0;
}
//递归回溯函数
// t记录当前放置的堡垒,lever是当前的行数,
// note记录该行暂时放置的堡垒数,以便修改t
void Blockhouses::Backtrack(int t,int lever)
{
int i,j,note;
if(lever>n)
{
if(t-1>max)
max=t-1;
}
else
{
//此循环是表示进入n叉树各个分支(以同一行的不同元素作为前一行各个元素的孩子)
for(i=1;i<=n;i++)
{
note=0;
//此循环是计算第lever行的堡垒
for(j=i;j<=n;j++)
{
if(a[lever][j]!='X')
{
x[t][1]=lever;
x[t][2]=j;
if(Place2(t))
{
t++;
note++;
}
}
}
//当拉进的t-1与t-2堡垒符合要求时才往下一层(行)搜索
if(Place(t-2) && Place(t-1))
Backtrack(t,lever+1);
t-=note;
//注意到当第lever行没有拉进堡垒时,让其直接往下一层搜索,
//即把第lever-1层与第lever+1层连起来
if((t-1>0) && (x[t-1][1]<lever))
Backtrack(t,lever+1);
}
}
}
//这函数是为了实现对成员的调用而设置
int nBlock(int n)
{
int i,j;
Blockhouses X;
X.max=0;
X.n=n;
int **p=new int*[n*n+1];
//p[](即x[])中用到p[][1]与p[][2]是要记录该堡垒的行列数
for(i=0;i<=n*n;i++) p[i]=new int[3];
for(i=0;i<=n*n;i++)
for(j=1;j<=2;j++)
p[i][j]=0;
X.x=p;
X.Backtrack(1,1);
for(i=0;i<=n*n;i++) delete []p[i]; //注意释放数组的形式
return X.max;
}
int main()
{
int n;
while(cin>>n && n!=0)
{
int i,j;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>a[i][j];
cout<<nBlock(n)<<endl;
}
return 0;
}