1079: [SCOI2008]着色方案
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 2237 Solved: 1361
[Submit][Status][Discuss]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 3Sample Output
10HINT
100%的数据满足:1 <= k <= 15, 1 <= ci <= 5
题解
令人恶心的一道 $DP$ ...
注意到 $k$ 的范围很小, 所以首先我们可以想到的定义状态的方案是把每种油漆剩余的数量定义进状态, 但是 $15$ 维的记忆化数组怕是是个人都不想写吧...而且$5^{15}approx 3.05 imes 10^{10}$ 并不能开得下...
但是我们会发现, 其实不同的油漆只要余量相等, 对于答案的影响并没有什么区别, 所以我们可以分别将余量为 $1,2,3,4,5$ 的油漆种数定义进状态, 再加一维表示上次用的是哪种油漆, $DFS$ 处理就好了
代码挺好写, 但看起来比较恶心...
参考代码
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 7 const int MOD=1e9+7; 8 const int MAXN=16; 9 10 int n; 11 int data[MAXN]; 12 int dp[MAXN][MAXN][MAXN][MAXN][MAXN][5]; 13 14 int DFS(int,int,int,int,int,int); 15 16 int main(){ 17 scanf("%d",&n); 18 int tmp; 19 for(int i=0;i<n;i++){ 20 scanf("%d",&tmp); 21 data[tmp]++; 22 } 23 for(int i=0;i<=5;i++){ 24 dp[0][0][0][0][0][i]=1; 25 } 26 printf("%d ",DFS(data[1],data[2],data[3],data[4],data[5],0)); 27 return 0; 28 } 29 30 int DFS(int r1,int r2,int r3,int r4,int r5,int last){ 31 if(dp[r1][r2][r3][r4][r5][last]!=0) 32 return dp[r1][r2][r3][r4][r5][last]; 33 else{ 34 long long tmp=0; 35 if(r1>0) 36 tmp+=1ll*(last==2?r1-1:r1)*DFS(r1-1,r2,r3,r4,r5,1); 37 if(r2>0) 38 tmp+=1ll*(last==3?r2-1:r2)*DFS(r1+1,r2-1,r3,r4,r5,2); 39 if(r3>0) 40 tmp+=1ll*(last==4?r3-1:r3)*DFS(r1,r2+1,r3-1,r4,r5,3); 41 if(r4>0) 42 tmp+=1ll*(last==5?r4-1:r4)*DFS(r1,r2,r3+1,r4-1,r5,4); 43 if(r5>0) 44 tmp+=1ll*r5*DFS(r1,r2,r3,r4+1,r5-1,5); 45 dp[r1][r2][r3][r4][r5][last]=tmp%MOD; 46 return dp[r1][r2][r3][r4][r5][last]; 47 } 48 }