题目链接:
记忆化搜索+(DP)。
首先看到数据范围就知道状态是个(15^5)的级别而不是(5^{15})了。
那么显然设(f_{a,b,c,d,e})表示还有(a)种颜色剩(1)个,(b)种剩两个,(cdots),(e)种剩(5)个时的方案数。
但是如何判断两个颜色不能相邻呢?
如果记录上一次的颜色,那么这次判断也需要颜色,则状态爆炸。
发现无论上次是什么颜色,只需要判断是否相同即可。
那么就记录上一次选的颜色现在还剩几个,在转移时如果相同那么方案数就要少一种选择。
综上,我们得到了如下的转移式:
[f_{a,b,c,d,e,PreRem}+=f_{a-1,b,c,d,e,0}*(a-[PreRem==1]) (a>0)
]
其中(PreRem)代表上一次选的颜色现在剩几个。
如果还有(a)个颜色(1),那么可以更新答案,(PreRem)变为(0),此时有(a)种颜色可选,故答案(*a)。
同时如果(PreRem==1),那么说明这(a)种颜色中有一种与之前重复,不能选,只剩(a-1)种颜色可选,答案(*(a-1))。
对于其他(4)项转移方程也同理(别忘记在低项加上刚用掉的颜色)。
时间复杂度 (O(k^c))
#include <cstdio>
#include <cstring>
int k,c[10];
int f[16][16][16][16][16][6];
const int Mod=1000000007;
int DP(int a,int b,int c,int d,int e,int PreRem)
{
if(!a&&!b&&!c&&!d&&!e)return 1;
int &Ans=f[a][b][c][d][e][PreRem];
if(~Ans)return Ans;
Ans=0;
if(a)(Ans+=DP(a-1,b,c,d,e,0)*1LL*(PreRem==1?a-1:a)%Mod)%=Mod;
if(b)(Ans+=DP(a+1,b-1,c,d,e,1)*1LL*(PreRem==2?b-1:b)%Mod)%=Mod;
if(c)(Ans+=DP(a,b+1,c-1,d,e,2)*1LL*(PreRem==3?c-1:c)%Mod)%=Mod;
if(d)(Ans+=DP(a,b,c+1,d-1,e,3)*1LL*(PreRem==4?d-1:d)%Mod)%=Mod;
if(e)(Ans+=DP(a,b,c,d+1,e-1,4)*1LL*(PreRem==5?e-1:e)%Mod)%=Mod;
return Ans;
}
int main()
{
scanf("%d",&k),memset(f,-1,sizeof f);
for(int cs;k--;)scanf("%d",&cs),++c[cs];
printf("%d
",DP(c[1],c[2],c[3],c[4],c[5],0));
return 0;
}