题目:https://daniu.luogu.org/problemnew/show/P2476
题目背景
四川NOI省选第二试
题目描述
有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i 种颜色的油漆足够涂ci 个木块。所有油漆刚好足够涂满所有木块,即c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。
输入输出格式
输入格式:第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck。
输出格式:输出一个整数,即方案总数模1,000,000,007的结果。
输入输出样例
输入样例#1:
3 1 2 3
输出样例#1:
10
输入样例#2:
5 2 2 2 2 2
输出样例#2:
39480
输入样例#3:
10 1 1 2 2 3 3 4 4 5 5
输出样例#3:
85937576
说明
50%的数据满足:1 <= k <= 5, 1 <= ci <= 3
100%的数据满足:1 <= k <= 15, 1 <= ci <= 5
解析
这属于比较简单的省选dp吧。
一开始看感觉下不去手,隐隐约约觉得是dp。
当看到ci<=5时眼前一亮。
这么小的范围,也许要有这么多维吧。
然后就想到了中国象棋这道题https://www.luogu.org/problemnew/show/2051
好了思路确立。
先想一想dfs的打法。
设a,b,c,d,e分别代表ci=1,ci=2,...,ci=5的颜色。
那么ans=dfs(a,b,c,d,e,last(回头说用处))。
我们考虑转移,
a,b,c,d,e的状态可以从
a-1,b,c,d,e
a+1,b-1,c,d,e(假设用了ci=2的其中一个,那么这个ci=2就会变成ci=1,所以a要+1,后面同理)
a,b+1,c-1,d,e
a,b,c+1,d-1,e
a,b,c,d+1,e-1
的状态转移过来。我们先单独考虑一下a,b,c,d,e转化到a+1,b-1,c,d,e的情况吧。
这相当于用了ci=2的颜色一个。
那么一共有几种可以用呢?
题目要求颜色相同的不能靠在一块。
我们设上次用的是last。
此处要用ci=2的,
如果上次用的是ci=3的,则所有ci=2中有一个颜色便是上次ci=3转移过来的。
如果这次再用这个,则相同颜色在一块了,可选数-1。
所以,dfs(a,b,c,d,e,last)转化成dfs(a+1,b-1,c,d,e,2)的贡献是:(b-(last==3))*dfs(a+1,b-1,c,d,e,2);
其余同理,dfs思路确立。
然后这个过程转化成记忆化即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define ll long long 8 #define mod 1000000007 9 ll k,re; 10 ll c[6]; 11 ll f[16][16][16][16][16][6]; 12 ll dfs(ll a,ll b,ll c,ll d,ll e,ll last){ 13 if (f[a][b][c][d][e][last]) return f[a][b][c][d][e][last]; 14 ll tot=0; 15 if (!a&&!b&&!c&&!d&&!e){ 16 f[0][0][0][0][0][last]=1; 17 return 1; 18 } 19 if (a) tot=(tot+((a-(last==2))*dfs(a-1,b,c,d,e,1))%mod)%mod; 20 if (b) tot=(tot+((b-(last==3))*dfs(a+1,b-1,c,d,e,2))%mod)%mod; 21 if (c) tot=(tot+((c-(last==4))*dfs(a,b+1,c-1,d,e,3))%mod)%mod; 22 if (d) tot=(tot+((d-(last==5))*dfs(a,b,c+1,d-1,e,4))%mod)%mod; 23 if (e) tot=(tot+(e*dfs(a,b,c,d+1,e-1,5))%mod)%mod; 24 return f[a][b][c][d][e][last]=tot; 25 } 26 int main(){ 27 scanf("%lld",&k); 28 for (int i=1;i<=k;++i){ 29 scanf("%lld",&re); 30 c[re]++; 31 } 32 printf("%lld",dfs(c[1],c[2],c[3],c[4],c[5],233666)); 33 return 0; 34 } 35