CF293B Distinct Paths 搜索

CF293B Distinct Paths

搜索。

LG传送门

看数据范围\(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;
}

事实上,这两种剪枝的依据都是:没有用到过的颜色对于搜索状态的影响是一样的,从而减少了重复计算。

猜你喜欢

转载自www.cnblogs.com/cj-chd/p/10302991.html