可以发现一个很好的性质:
由于
1≤n≤4,1≤m≤7
1
≤
n
≤
4
,
1
≤
m
≤
7
,
因此一个合法的矩阵最多有
⌈42⌉×⌈72⌉=8
⌈
4
2
⌉
×
⌈
7
2
⌉
=
8
个X,考虑将
1
1
至的数从小到大填入矩阵。状压DP。
定义状态:
f[i][S]
f
[
i
]
[
S
]
表示:
1
1
到的数已经填入矩阵。
S
S
是一个用二进制数表示的集合,表示所有X格子的状态,某一位为表示对应的格子已经被填入,否则为
0
0
表示没有被填入。在以上条件下的方案数。
转移就是填入一个数。下面用图来分析一下。
下面,红色格子表示已经被填入的X格子,蓝色格子表示还未被填入的X格子:
显然,下面的阴影部分(蓝色格子周围
8
8
个方向的格子)在这一步是不能填数的,否则就破坏了性质:
因此可以填在蓝色格子里,也可以填在阴影部分和蓝色格子之外的空格里。
其中,如果
i+1
i
+
1
填在蓝色格子里,集合
S
S
就会扩充。
因此有两种转移:
(1)填在蓝色格子(为X格子的个数):
其中 1≤j≤d 1 ≤ j ≤ d 并且 j∉S j ∉ S 。
(2)填在阴影和蓝色格子之外的空格里(下面 area a r e a 为这样的空格的个数,可以计算出):
最后结果为 f[n×m][{1,2,...,d}] f [ n × m ] [ { 1 , 2 , . . . , d } ] 。
但这个方程是假的,还需要解决的一个问题在于:
这个方程只能保证X格子是局部极小值, 但不能保证“.”格子不是局部极小值。
因此考虑容斥原理:暴力 2... 2 . . . 枚举每个“.”格子,强制把这些格子变成X,然后每次都做一遍DP。看上去复杂度是爆炸的,但是实际上任意两个X格子都不能相邻。利用这一性质缩小枚举范围,就比较科学了。
答案的统计方法是:
最后答案 = = 有个X格子的方案数 − − 有个X格子的方案数 + + 有个X格子的方案数 −... − . . .
复杂度
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
const int L = 6, R = 9, N = 30, C = (1 << 8) + 5, MX = 12345678;
int n, m, ans, dx[] = {-1, -1, 1, 1, 0, 0, -1, 1, 0}, dy[] =
{-1, 1, -1, 1, -1, 1, 0, 0, 0}, f[N][C], area[C], iX[N], iY[N];
char s[L][R];
bool vis[L][R];
void solve(int cnt)
{
int i, j, k, Cm, orz = 0;
memset(f, 0, sizeof(f));
f[0][0] = 1;
For (i, 1, n) For (j, 1, m)
if (s[i][j] == 'X') iX[++orz] = i, iY[orz] = j;
Cm = 1 << orz;
For (i, 0, Cm - 1)
{
memset(vis, 0, sizeof(vis));
For (j, 1, orz)
{
if (!(((i >> j - 1)) & 1)) continue;
For (k, 0, 8)
{
int tx = iX[j] + dx[k], ty = iY[j] + dy[k];
if (tx >= 1 && tx <= n && ty >= 1 && ty <= m)
vis[tx][ty] = 1;
}
}
area[i] = 0;
For (j, 1, n) For(k, 1, m)
if (!vis[j][k]) area[i]++;
}
For (i, 0, n * m - 1)
For (j, 0, Cm - 1)
{
For (k, 1, orz)
if (!((j >> k - 1) & 1))
f[i + 1][j | (1 << k - 1)] = (f[i + 1]
[j | (1 << k - 1)] + f[i][j]) % MX;
f[i + 1][j] = (f[i + 1][j] + 1ll * (area[Cm - j - 1] - i)
* f[i][j] % MX) % MX;
}
if (cnt & 1) ans = (ans - f[n * m][Cm - 1] + MX) % MX;
else ans = (ans + f[n * m][Cm - 1]) % MX;
}
void dfs(int dep, int cnt)
{
if (dep == n * m + 1) return solve(cnt);
int i, x = (dep - 1) / m + 1, y = (dep - 1) % m + 1;
bool flag = 1;
For (i, 0, 8)
{
int tx = x + dx[i], ty = y + dy[i];
if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && s[tx][ty] == 'X')
flag = 0;
}
dfs(dep + 1, cnt);
if (!flag) return;
s[x][y] = 'X'; dfs(dep + 1, cnt + 1);
s[x][y] = '.';
}
int main()
{
int i;
cin >> n >> m;
For (i, 1, n) scanf("%s", s[i] + 1);
dfs(1, 0);
cout << ans << endl;
return 0;
}