【BZOJ1079】【SCOI2008】着色方案

Time Limit: 10 Sec Memory Limit: 162 MB

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 3
  

Sample Output

  
  10
  

HINT

  
  100%的数据满足:1 <= k <= 15, 1 <= ci <= 5
  
  
  

Solution

  
  ​ 是不是还想状压啊,行不通的,状态数太多了。
  
​   看看还有什么是比较小的:\(c_i\)看起来很小。
  
​   仔细想一想会发现,如果两种颜料的可使用次数相同,那么它们可以归为一类颜料。
  
  ​ 从可使用次数分类入手,设状态\(f[a_1][a_2][a_3][a_4][a_5][x]\),表示当前已使用\(i\)次的颜料有\(a_i\)种,且下一步将要选择当前可使用\(x\)次的颜料,的总方案数。
  
  ​ 干脆就自顶向下使用记忆化搜索DP实现。每个状态从哪里转移就很显然了:可以挑5类颜料中的一种,那么转移的来源就是\(\{a_1-1,a_2,a_3,a_4,a_5\}\),\(\{a_1+1,a_2-1,a_3,a_4,a_5\}\),\(\{a_1,a_2+1,a_3-1,a_4,a_5\}\),\(\{a_1,a_2,a_3+1,a_4-1,a_5\}\),\(\{a_1,a_2,a_3,a_4+1,a_5-1\}\)
  
​   关键是转移的系数和递归参数。简单的讲,每类颜料\(i\)\(a_i\)种不同的选法,但这没有考虑到题目要求两两不同给的限制。
  
​   注意每个状态的\(a_i\)是相对于当前状态下的信息,而\(x\)则是上一层状态给你做出的限制。你枚举当前这一步将要挑哪一类颜料,递归的时候就要把这类颜料作为下一步的\(x\)传进去,表示你下一层自己选择的时候,如果要选我这一层选择的这类颜料,你可用的选择数必须减1,否则将会和我冲突。而上一层递归给这一层传进了\(x\),说明上一层递归所挑的颜料在上一层为第\(x\)类,那么在这一层则是第\(x-1\)类(没用之前即减1)。如果你枚举当前层选择了第\(x-1\)类颜料,可用方案数必须减1。
  
​   需要梳理一下思路和控制关系,实现起来并不难。
  
  
  

#include <cstdio>
#include <cstring>
using namespace std;
const int N=16,MOD=1e9+7;
int n,a[6],c[6];
int f[N][N][N][N][N][6];
int dfs(int a1,int a2,int a3,int a4,int a5,int x){
    int &F=f[a1][a2][a3][a4][a5][x];
    if(F!=-1) return F;
    F=0;
    if(a1)
        (F+=1LL*dfs(a1-1,a2,a3,a4,a5,1)*(a1-(x-1==1))%MOD)%=MOD;
    if(a2)
        (F+=1LL*dfs(a1+1,a2-1,a3,a4,a5,2)*(a2-(x-1==2))%MOD)%=MOD;
    if(a3)
        (F+=1LL*dfs(a1,a2+1,a3-1,a4,a5,3)*(a3-(x-1==3))%MOD)%=MOD;
    if(a4)
        (F+=1LL*dfs(a1,a2,a3+1,a4-1,a5,4)*(a4-(x-1==4))%MOD)%=MOD;
    if(a5)
        (F+=1LL*dfs(a1,a2,a3,a4+1,a5-1,5)*a5%MOD)%=MOD;
    return F;
}
int main(){
    freopen("input.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        c[x]++;
    }
    memset(f,-1,sizeof f);
    for(int i=0;i<=5;i++)
        f[0][0][0][0][0][i]=1;
    printf("%d\n",dfs(c[1],c[2],c[3],c[4],c[5],0));
    return 0;
}
posted @ 2018-04-16 16:59  RogerDTZ  阅读(...)  评论(... 编辑 收藏