题目大意
给定一个n*m的矩阵,每个格子内允许填 1−k(k<10) ,问有多少种填数方案可以让每一条从左上到右下的路径不经过数字相同的点
解答
搜索剪枝,暴搜每个点,首先用 ¬(F[x−1][y]∨F[x][y−1]) 得到所有可以用的数码,用二进制记每个数是否用过,每一次用 i∧(−i) 得到小的一个可以用的数,最后从可用集合中减掉就可以了,对于这个点首次用某个数字(之前没有填过并且给的矩阵里没有出现这个数字)的所有情况得到的解实际上是相同的,所以我们只需要搜一遍就可以了。具体请看代码第31-34行。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MOD = 1000000007;
int n, m, k;
int col[15][15];
int f[15][15];
int Log2[1<<12];
int use[15];
int dfs(int x, int y)
{
if (y > m)
x++, y = 1;
if (x > n)
return 1;
int fir = -1, tmp, zt, ret = 0;
zt = f[x][y-1] | f[x-1][y];
for (int i = (~zt)&((1<<k)-1); i; i -= (i&(-i))) {
tmp = Log2[i & (-i)];
if (col[x][y] == 0 || col[x][y] == tmp) {
use[tmp]++;
f[x][y] = zt | (1 << (tmp-1));
if (use[tmp] == 1) {
if (fir == -1)
fir = dfs(x, y+1);
ret += fir;
} else {
ret += dfs(x, y+1);
}
ret %= MOD;
use[tmp]--;
}
}
return ret;
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> k;
if (n + m - 1 > k) {
cout << "0";
return 0;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> col[i][j];
use[col[i][j]]++;
}
int tmp = 1;
for (int i = 1; i <= k; i++) {
Log2[tmp] = i;
tmp *= 2;
}
cout << dfs(1, 1);
return 0;
}