BZOJ1079 [SCOI2008]着色方案 记忆化搜索DP 妙啊

来源:互联网 发布:python 英文词性标注 编辑:程序博客网 时间:2024/05/29 07:58

大家都很强, 可与之共勉 。

不看题解不会,看了秒题系列

着色方案
Description

  有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i种颜色的油漆足够涂ci个木块。
所有油漆刚好足够涂满所有木块,即c1+c2+…+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两
个相邻木块颜色不同的着色方案。

Input

  第一行为一个正整数k,第二行包含k个整数c1, c2, … , ck。

Output

  输出一个整数,即方案总数模1,000,000,007的结果。

Sample Input

3

1 2 3
Sample Output

10
HINT

100%的数据满足:1 <= k <= 15, 1 <= ci <= 5

f[a][b][c][d][e][x]表示能涂一个格子的油漆有a种,能涂两个格子的油漆有b种…x表示的是上一次涂的是能涂x个格子的油漆;

f[a][b][c][d][e][x]=f[a-1][b][c][d][e](a-(x==2))+f[a+1][b-1][c][d][e](b-(x==3))+…+f[a][b][c][d+1][e-1]*(a-(x==6));

这个方程表示,剩下a,b,c,d,e,上次的是x,

假设这次要用可以涂k个格子的油漆,那么涂完这个格子,可以涂k个格子的油漆少了一种,可以涂k-1个格子的油漆多了一种;

这就是为什么b-1的时候a+1,c-1的时候b+1的原因;

还有就是相邻的格子的颜色不同,假设上一次用的是可以涂x个格子的油漆,那么上一次用完后的那种油漆就变成了一种可以涂x-1个格子的油漆了,那么这一次再选油漆的时候就不能再选可以涂x-1个格子的油漆的一种(这种上次一就用过了);所以对应的是(a-(x==2))和(b-(x==3))这些了。

# include <cstdio>const int Mod = 1000000007 ;int n, cnt [6] ;long long dp [16] [16] [16] [16] [16] [6] ;inline long long DP ( int a1, int a2, int a3, int a4, int a5, int last )  {        if( ( a1 | a2 | a3 | a4 | a5 ) == 0 )  return dp [a1] [a2] [a3] [a4] [a5] [last] = 1 ;    if ( dp [a1] [a2] [a3] [a4] [a5] [last] ) return dp [a1] [a2] [a3] [a4] [a5] [last] ;    long long now ( 0 ) ;    if ( a1 ) now += ( a1 - ( last == 2 ) ) * DP ( a1 - 1, a2, a3, a4, a5, 1 ), now %= Mod ;    if ( a2 ) now += ( a2 - ( last == 3 ) ) * DP ( a1 + 1, a2 - 1, a3, a4, a5, 2 ), now %= Mod ;    if ( a3 ) now += ( a3 - ( last == 4 ) ) * DP ( a1, a2 + 1, a3 - 1, a4, a5, 3 ), now %= Mod ;    if ( a4 ) now += ( a4 - ( last == 5 ) ) * DP ( a1, a2, a3 + 1, a4 - 1, a5, 4 ), now %= Mod ;    if ( a5 ) now += a5 * DP ( a1, a2, a3, a4 + 1, a5 - 1, 5 ) , now %= Mod ;    return dp [a1] [a2] [a3] [a4] [a5] [last] = now ;}int main ( )  {    scanf ( "%d", & n ) ;    for ( int i = 0, x ; i < n ; ++ i ) scanf ( "%d", & x ), ++ cnt [x] ;    return printf ( "%lld\n", DP ( cnt [1], cnt [2], cnt [3], cnt [4], cnt [5], 0 ) ), 0 ;}