洛谷P2476 [SCOI2008]着色方案

题目: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                     
View Code
原文地址:https://www.cnblogs.com/gjc1124646822/p/7899419.html