类似“八皇后问题”,但加了“墙”的概念,如果有墙相隔,则同一行墙两边可同时存在两个元素。
枚举子集+判断可行性,在枚举子集时,可以进行适当剪枝,如此可以减少很多不必要的枚举,比如:当n == 4时,其最大的的“车”的位置为8个,则我们可以加一个计数器,最多将子集的枚举量设为8,超过8则不再往子集中加入元素。
代码如下:
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
int n, num, num_1, cct;
int flag[20],save[5] = {0, 2, 3, 6, 9}; // n为1,2,3,4时对应的最大可行的“位置”个数
char a[5][5], c[5][5];
struct point
{
int x, y;
} po[20];
int judge() // 判断子集是否可行
{
memcpy(c, a, sizeof(a));
for(int i = 0; i < num; i++)
if(flag[i])
c[po[i].x][po[i].y] = '*';
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(c[i][j] == '*')
{
for(int ii = i + 1, fl = 1; ii < n; ii++)
{
if(c[ii][j] == 'X')
fl = 0;
else if(c[ii][j] == '*' && fl)
return 0;
}
for(int jj = j + 1, fl = 1; jj < n; jj++)
{
if(c[i][jj] == 'X')
fl = 0;
else if(c[i][jj] == '*' && fl)
return 0;
}
}
return 1;
}
int subset(int cur) // 对所有‘.’的位置进行枚举
{
if(cur == num)
{
int fl = judge();
if(fl && num_1 > cct)
cct = num_1;
return fl;
}
flag[cur] = 1;
++num_1;
if(num_1 < save[n])
subset(cur + 1);
--num_1;
flag[cur] = 0;
subset(cur + 1);
return 0;
}
int main()
{
#ifdef test
freopen("sample.txt", "r", stdin);
#endif
while(scanf("%d", &n), n)
{
cct = num_1 = num = 0;
for(int i = 0; i < n; i++)
scanf("%s", a[i]);
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(a[i][j] == '.')
{
po[num].x = i;
po[num++].y = j;
}
subset(0);
printf("%d\n", cct);
}
return 0;
}