CF293B Distinct Paths
搜索。
看数据范围(n,m)显然是不可能到(1000)的,可以知道当(n + m - 1 > k)时无解。一下就把数据范围降到(10)。
考虑枚举每个点的颜色,每个位置的颜色不能和它左边和上方的颜色相同,现在需要快速计算填每种颜色的方案数,到这里分出了两种写法。
一种是对于每种没有使用过的颜色都只算一次,像下面这样:
#include <cstdio>
#include <cctype>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 13, M = 10003, yyb = 1e9 + 7;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
R int f = 0;
R char c = gc();
while (c < 48 || c > 57)
c = gc();
while (c > 47 && c < 58)
f = f * 10 + (c ^ 48), c = gc();
return f;
}
int lg[M], col[N][N], f[N][N], use[N], n, m, k;
int dfs(int x, int y) {
if (y > m)
++x, y = 1;
if (x > n)
return 1;
R int fir = -1, tmp, now = f[x][y - 1] | f[x - 1][y], o = 0, i;
for (i = (~now) & ((1 << k) - 1); i; i ^= i & -i) {
tmp = lg[i & -i];
if (col[x][y] == 0 || col[x][y] == tmp) {
++use[tmp], f[x][y] = now | (1 << tmp - 1);
if (use[tmp] == 1) {
if (fir == -1)
fir = dfs(x, y + 1);
o += fir;
}
else
o += dfs(x, y + 1);
o %= yyb, --use[tmp];
}
}
return o;
}
int main() {
R int i, j;
n = rd(), m = rd(), k = rd();
if (n + m - 1 > k) {
printf("0");
return 0;
}
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
col[i][j] = rd(), ++use[col[i][j]];
for (i = 1, j = 1; j <= k ; i <<= 1, ++j)
lg[i] = j;
printf("%d", dfs(1, 1));
return 0;
}
一种是处理出用了多少种本来没有使用过的颜色,设总的没有使用过的颜色是(y)个,用了(x)个,计算贡献的时候就乘个(P _ y ^ x),即在(y)种颜色中选(x)个排到需要用的(x)个地方,像下面这样:
#include <cstdio>
#include <cctype>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 13, yyb = 1e9 + 7;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
R int f = 0;
R char c = gc();
while (c < 48 || c > 57)
c = gc();
while (c > 47 && c < 58)
f = f * 10 + (c ^ 48), c = gc();
return f;
}
int a[N][N], b[N], f[N], p, n, m, k, ans;
void dfs(int x, int y, int q) {
if (y > m)
return dfs(x + 1, 1, q);
if (x > n) {
ans = (ans + f[p] / f[p - q]) % yyb;
return ;
}
if (a[x][y])
return dfs(x, y + 1, q);
R int c[N], i, j, t;
for (i = 1; i <= k; ++i)
c[i] = 0;
for (i = 1; i <= x; ++i)
for (j = 1; j <= y; ++j)
c[a[i][j]] = 1;
for (i = x; i <= n; ++i)
for (j = y; j <= m; ++j)
c[a[i][j]] = 1;
for (i = 1, j = 0, t = 0; i <= k; ++i) {
if (b[i] ^ 1)
++j;
if ((!c[i]) && ((b[i] == 1) || (j <= q + 1))) {
if ((!b[i]) && (j == q + 1))
t = 1, b[i] = 2;
a[x][y] = i, dfs(x, y + 1, q + t);
if (t)
b[i] = 0, t = 0;
}
}
a[x][y] = 0;
}
int main() {
n = rd(), m = rd(), k = rd();
if (n + m - 1 > k) {
printf("0");
return 0;
}
R int i, j, x, y;
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
a[i][j] = rd();
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
if (a[i][j]) {
b[a[i][j]] = 1;
for (x = i; x <= n; ++x)
for (y = j; y <= m; ++y)
if (((i ^ x) || (j ^ y)) && a[i][j] == a[x][y]) {
printf("0");
return 0;
}
}
f[0] = 1;
for (i = 1; i <= k; ++i)
p += (!b[i]), f[i] = f[i - 1] * i;
dfs(1, 1, 0), printf("%d", ans);
return 0;
}
事实上,这两种剪枝的依据都是:没有用到过的颜色对于搜索状态的影响是一样的,从而减少了重复计算。