[SCOI2008] 着色方案(待续)
题目大意:一排(n)个木块,(k)种油漆,每种油漆有(c_i)桶,要求相邻两个木块的颜色不能相同,求着色方案(pmod{1e9+7})
Solution.1
状态:设(f[i][j])为前(i)块被涂色的方案,且最后一块是(j)颜色转移:$f[i][j] = sum_{h=1,h e j } ^ {k}f[i-1][h] $
好的,记录不了每桶用了多少,挂掉。
诶,这个(c[i])有点小啊,而且如果两种油漆剩下相同的桶数,说明
- 状态:设(f[a][b][c][d][e][last])为还剩(1)桶的有(a)种,还剩(2)桶的有(b)种(dots),还剩(5)桶的有(e)种,最后一个是上一次还剩(k)桶的油漆的方案数。
- 转移:详见代码
Code.1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
typedef long long ll;
const int Mod = 1e9 + 7;
int x[6];
ll f[16][16][16][16][16][6];
ll dp(int a, int b, int c, int d, int e, int k){
if(a + b + c + d + e == 0) return 1;//都用光了
if(f[a][b][c][d][e][k]) return f[a][b][c][d][e][k] % Mod;//记搜
ll t = 0;
if(a)
t = (t + 1LL * (a - (k == 2)) * dp(a - 1, b, c, d, e, 1)) % Mod;
if(b)
t = (t + 1LL * (b - (k == 3)) * dp(a + 1, b - 1, c, d, e, 2)) % Mod;
if(c)
t = (t + 1LL * (c - (k == 4)) * dp(a, b + 1, c - 1, d, e, 3)) % Mod;
if(d)
t = (t + 1LL * (d - (k == 5)) * dp(a, b, c + 1, d - 1, e, 4)) % Mod;
if(e)
t = (t + 1LL * e * dp(a, b, c, d + 1, e - 1, 5)) % Mod;
f[a][b][c][d][e][k] = t % Mod;//假设上一次用的是可以涂k个格子的油漆,那么上一次用完后,那种油漆就只可以涂k - 1个格子了,那么这一次涂油漆的时候就不能再选可以涂k - 1个格子的油漆的一种,因为这种上次已经用过了,且相同的油漆不能相邻。
return t % Mod;
}
int main(){
int k;
scanf("%d", &k);
for(int i = 1, la; i <= k; ++i){
scanf("%d", &la);
x[la] ++;
}
printf("%lld
", dp(x[1], x[2], x[3], x[4], x[5], 0) % Mod);
return 0;
}
Solution.2
排列组合搞(DP)
- 状态:(f[i][j])表示使用了前(i)种油漆,有(j)个相邻的同色快的方案数
考虑新加一种油漆(i+1),我们假设有(x)个,我们先将他们分组。
设分成了(q)组,用的插板和插空思想,有(q-1)个板子,有(x-1)个空,那么方案数就是(C_{x-1}^{q-1}).
并会产生(x-q)个相邻的色块,因为如果不分组,那么会产生(x-1)个相邻色块,分成(q)组,会减少(q-1)个相邻色块(手动画一画),所以最终产生的相邻色块是((x-1)-(q-1)=x-q)个
下一步我们要把分好的组插入原来的木块中间(包括两边),所以总共就有(sum[i]+1)个空,其中有(j)个空两边颜色相同,我们用这(q)组中的(b)个插入左右颜色相同的位置,(C_{j}^{b})就是插入到之前同色的之间的方案数(位置不同),(C_{sum[i]+1−j}^{q−b})就是相当于前面有(sum[i]+1)个位置,(j)个相邻块占据的位置不能放,(q-b)插入进去的方案数
最后剩下的相邻色块就是(j-b+(x-q))
[f[i + 1][j - b + (x - q)] = C_{x-1}^{q-1}cdot C_{sum[i]+1−j}^{q−b} cdot C_j^bcdot f[i][j]
]
Code.2
#include <bits/stdc++.h>
using namespace std;
const int N = 106;
const int P = 1e9 + 7;
int k;
int a[N], sum[N];
int c[N][N];
int f[N][N];
inline void pre_work() {
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] + c[i - 1][j - 1]) % P;
}
return;
}
int main() {
scanf("%d", &k);
for(int i = 1; i <= k; ++i) {
scanf("%d", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
pre_work();
f[1][a[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 qwq = 1; qwq <= a[i + 1]; ++qwq)//最多能分成多少组
for(int b = 0; b <= qwq && b <= j; ++b){//从qwq里面调不大于j的b个插到之前色块相邻的地方
int res = 1LL * f[i][j] * c[a[i + 1] - 1][qwq - 1] % P * c[j][b] % P;
res = 1LL * c[sum[i] + 1 - j][qwq - b] % P * res % P;//剩下的
f[i + 1][j + a[i + 1] - qwq - b] = (f[i + 1][j + a[i + 1] - qwq - b] + res) % P;
}
}
printf("%d
", f[k][0]);
return 0;
}
2018-09-24 14:39