题目大意:有 $n$ 个木块排成一排,从左到右依次编号为 $1\cdots n$。你有 $k$ 种颜色的油漆,其中第 $i$ 种颜色的油漆足够涂 $c_i$ 个木块。所有油漆刚好足够涂满所有木块,即 $c_1+c_2+…+c_k=n$。统计任意两个相邻木块颜色不同的着色方案,输出方案数模 $1,000,000,007$ 的结果。$1 <= k <= 15, 1 <= c_i <= 5$
#1
设 $f[a][b][c][d][e][x]$ 表示目前还能涂 $1$ 次的颜色有 $a$ 个,能涂 $2$ 次的颜色有 $b$ 个……能涂 $5$ 次的有 $e$ 个,上一次用的是能涂 $x$ 次的颜色。
#include <cstdio>
#include <cstring>
const int P = 1000000007;
int a[6], f[16][16][16][16][16][6], k;
int dfs(int a, int b, int c, int d, int e, int x) {
if (f[a][b][c][d][e][x] != -1) return f[a][b][c][d][e][x];
if (!a && !b && !c && !d && !e) return 1;
f[a][b][c][d][e][x] = 0;
if (a) (f[a][b][c][d][e][x] += 1LL * (a - (x == 2)) * dfs(a - 1, b, c, d, e, 1) % P) %= P;
if (b) (f[a][b][c][d][e][x] += 1LL * (b - (x == 3)) * dfs(a + 1, b - 1, c, d, e, 2) % P) %= P;
if (c) (f[a][b][c][d][e][x] += 1LL * (c - (x == 4)) * dfs(a, b + 1, c - 1, d, e, 3) % P) %= P;
if (d) (f[a][b][c][d][e][x] += 1LL * (d - (x == 5)) * dfs(a, b, c + 1, d - 1, e, 4) % P) %= P;
if (e) (f[a][b][c][d][e][x] += 1LL * e * dfs(a, b, c, d + 1, e - 1, 5) % P) %= P;
return f[a][b][c][d][e][x];
}
int main() {
scanf("%d", &k);
for (int i = 1; i <= k; ++i) {
int x; scanf("%d", &x); ++a[x];
}
memset(f, -1, sizeof f);
printf("%d\n", dfs(a[1], a[2], a[3], a[4], a[5], 0));
return 0;
}
时间复杂度 $O(k^5c)$
#2
设 $f[i][j]$ 表示前 $i$ 种颜色全用完,有 $j$ 对相邻的同色块的方案数。
考虑将 $c[i+1]$ 分成 $a$ 组的插入到已经弄好的块中,其中 $b$ 组插入到之前同色的之间,$a-b$ 组插空放不相邻,有状态转移方程(其中 $sum[i]=\sum\limits_{k=1}^{i}c_k$)
其中 $j-b+c_{i+1}-a$ 的意思是,到第 $i+1$ 个颜色,把这个颜色分成 $a$ 组后,产生了 $c_{i+1}-a$ 对相邻的同色块(组与组之间互不相邻),$b$ 组插入到之前同色的之间,就减少了 $b$ 对同色块。
#include <cstdio>
const int N = 106, P = 1e9+7;
int C[N][N], f[N][N], c[N], sum[N], k;
void get_c() {
C[0][0] = 1;
for (int i = 1; i <= 75; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j)
C[i][j] = (C[i-1][j-1] + C[i-1][j]) % P;
}
}
int main() {
scanf("%d", &k);
for (int i = 1; i <= k; ++i) {
scanf("%d", &c[i]);
sum[i] = sum[i-1] + c[i];
}
get_c();
f[1][c[1]-1] = 1; //a[1]-1是最多对数
for (int i = 1; i < k; ++i)
for (int j = 0; j < sum[i]; ++j) {
if (!f[i][j]) continue;
for (int a = 1; a <= c[i+1]; ++a)
for (int b = 0; b <= a && b <= j; ++b)
(f[i+1][j+c[i+1]-a-b] += 1LL * f[i][j] * C[c[i+1]-1][a-1] % P * C[j][b] % P * C[sum[i]-j+1][a-b] % P) %= P;
}
printf("%d\n", f[k][0]);
return 0;
}
时间复杂度 $O(k^2c^3)$