因为这类问题都需要从前一步来推后一步,所以大概率是DP类问题
首先我们需要确定状态,如果把每种颜色都当成一维来记录的话,最大是 维肯定是不可取的,所以就要考虑别的状态
因为每种颜色 ,我们可以把每种颜色剩余能涂的个数看成一个等价类 来确定每种状态
其中,
表示在所有颜色中剩余能涂的个数为
的,有
个颜色(颜色之间有相同,也有不同)
同理,
代表上一次用的是那种等价类里面的颜色
如果把不同颜色,按照剩余能涂的个数来划分到一个等价类,会不会对答案造成影响?,不会
因为我们每一步取的颜色,对于它来说,肯定会改变它的状态,所以每一步取的颜色肯定都不相同
例如 当前有 种颜色
它们都剩余了
个,所以
这些颜色一共
个 ,其中
是相互独立的
我们从 中拿出一个颜色(可以不知道取的是那种颜色)之后
,
因为其中有一种颜色(我们不关心掉下去的颜色是谁,只关心这个等价类)从原先的剩余
个变成了
个
这就是他们相互独立的原因,也是可以这样分类来取的原因。还要记得判断一下
if(c2) res += (c2 - (lst == 3)) * dfs(c1 + 1,c2 - 1,c3,c4,c5,2);
这段代码的意思是,如果 还能取,就把这种情况加到
中,如果上次取的是
那么就会从
中掉一种颜色到
所以,我们要避开这种相同颜色,只能取剩下的
种了,取了
种后,下一步就是
然后根据乘法原理搞就行
最后相加取模即可
if(c5) res += c5 * dfs(c1,c2,c3,c4 + 1,c5 - 1,5);
因为不可能有 所以就不用考虑
的影响了,直接搞
#include <bits/stdc++.h> using namespace std; const int N = 16,mod = 1e9 + 7; typedef long long LL; int f[N][N][N][N][N][6],k,c[N],x,n; LL dfs(int c1,int c2,int c3,int c4,int c5,int lst) { // 如果这种状态被记录过,直接返回即可,不用计算 if(f[c1][c2][c3][c4][c5][lst]) return f[c1][c2][c3][c4][c5][lst]; LL res = 0; if(c1) res += (c1 - (lst == 2)) * dfs(c1 - 1,c2,c3,c4,c5,1); if(c2) res += (c2 - (lst == 3)) * dfs(c1 + 1,c2 - 1,c3,c4,c5,2); if(c3) res += (c3 - (lst == 4)) * dfs(c1,c2 + 1,c3 - 1,c4,c5,3); if(c4) res += (c4 - (lst == 5)) * dfs(c1,c2,c3 + 1,c4 - 1,c5,4); if(c5) res += c5 * dfs(c1,c2,c3,c4 + 1,c5 - 1,5); return f[c1][c2][c3][c4][c5][lst] = res % mod; } int main() { // 边界条件 for(int i = 1;i <= 5; ++i) f[0][0][0][0][0][i] = 1; cin >> n; for(int i = 1;i <= n; ++i) { cin >> x;// 读入第 i 种颜色的个数 c[x] ++;// 能涂 i 个木块的颜色一共有 c[x] 个 } cout << dfs(c[1],c[2],c[3],c[4],c[5],0) << endl; return 0; }