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;
}