【SCOI2008】着色方案

有n个木块排成一行,从左到右依次编号为1~n。你有k种颜色的油漆,其中第i 种颜色的油漆足够涂ci 个木块。所有油漆刚好足够涂满所有木块,即 c1+c2+...+ck=n。相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。

Input

第一行为一个正整数k,第二行包含k个整数c1, c2, ... , ck。

Output

输出一个整数,即方案总数模1,000,000,007的结果。

Sample Input

样例1: 

1 2 3 
样例2: 

2 2 2 2 2 
样例3: 
10 
1 1 2 2 3 3 4 4 5 5

题解:
这个题目我们可以试着用记忆化搜索,状态设置的十分巧妙,首先我们可以看到ci是小于等于5的吧,并且只要数量一样,
那么颜色对答案的贡献其实就是一样的,所有’我们可以定义一个五维的数组F[a1][a2][a3][a4][a5][h]表示数量为1的颜料数是
a1,数量为2的颜色的颜色数为a2.......的最大方案数,然后转移的话看我代码吧,转移时要注意,最后一位是存上一次决策,
如果上个决策是用剩余数为4的颜色,那么显然剩余数为3的颜色有一种是从4转移的,此时再用这种颜色就会染上相同的颜色。
注意一下就好了。
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#define ll long long
const int MOD=1000000007;
using namespace std;
int num[6];
ll f[16][16][16][16][16][6];
ll dp(int a1,int a2,int a3,int a4,int a5,int last){
  ll now=0;
  if((a1 | a2 | a3 | a4 | a5)==0) return f[a1][a2][a3][a4][a5][last]=1;
  if(f[a1][a2][a3][a4][a5][last]) return f[a1][a2][a3][a4][a5][last];
  if(a1) now+=(a1-(last==2))*dp(a1-1,a2,a3,a4,a5,1);now%=MOD;
  if(a2) now+=(a2-(last==3))*dp(a1+1,a2-1,a3,a4,a5,2);now%=MOD;
  if(a3) now+=(a3-(last==4))*dp(a1,a2+1,a3-1,a4,a5,3);now%=MOD;
  if(a4) now+=(a4-(last==5))*dp(a1,a2,a3+1,a4-1,a5,4);now%=MOD;
  if(a5) now+=a5*dp(a1,a2,a3,a4+1,a5-1,5);now%=MOD;
  f[a1][a2][a3][a4][a5][last]=now;now%=MOD;
  return now;
}
int main(){
  int n;
  memset(f,0,sizeof(f));
  scanf("%d",&n);
  for(int i=1;i<=n;i++){int x;scanf("%d",&x);num[x]++;}
  printf("%lld",dp(num[1],num[2],num[3],num[4],num[5],0));
  return 0;
}

 

posted @ 2017-07-06 19:55  人间失格—太宰治  阅读(247)  评论(0编辑  收藏  举报