BZOJ 1079: [SCOI2008]着色方案【记忆化DFS】

1079: [SCOI2008]着色方案

Time Limit: 10 Sec Memory Limit: 162 MB

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

题解

拿到题,一个5^15的DP肯定是有的,但是这题不重视你涂的颜色,只注重颜色的个数,那我们不妨写成有几种颜色是可以图X次的,所以就可以设f[A][B][C][D][E]表示还能涂一次的颜色个数有A个,能涂二次的颜色个数有B个……
然后用记忆化DFS实现求解。

代码如下

#include<cstdio>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int tt=1000000007;
int n,a[10];
LL f[16][16][16][16][16][6];
LL DFS(int a,int b,int c,int d,int e,int lst){
    LL ans=0;
    if(!a&&!b&&!c&&!d&&!e) return 1;
    if(f[a][b][c][d][e][lst]^-1) return f[a][b][c][d][e][lst];
    if(a>0) ans=(ans+DFS(a-1,b,c,d,e,1)*(a-(lst==2)))%tt;//如果lst等于2,说明上次用的颜色在1中,所以1需要减少一个颜色。 
    if(b>0) ans=(ans+DFS(a+1,b-1,c,d,e,2)*(b-(lst==3)))%tt;//下同 
    if(c>0) ans=(ans+DFS(a,b+1,c-1,d,e,3)*(c-(lst==4)))%tt;
    if(d>0) ans=(ans+DFS(a,b,c+1,d-1,e,4)*(d-(lst==5)))%tt;
    if(e>0) ans=(ans+DFS(a,b,c,d+1,e-1,5)*e)%tt;//第5个肯定不需要了 
    return f[a][b][c][d][e][lst]=ans;
}
int main(){
    memset(f,-1,sizeof(f));
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        a[x]++;
    }
    printf("%lld\n",DFS(a[1],a[2],a[3],a[4],a[5],0));
    return 0;
} 
posted @ 2018-03-18 13:17  XSamsara  阅读(125)  评论(0编辑  收藏  举报