SCOI2008 着色方案(dp)

Description

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

Input

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

Output

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

Sample Input

3

1 2 3

Sample Output

10

HINT

50%的数据满足: 1k5,1ci3 1 ≤ k ≤ 5 , 1 ≤ c i ≤ 3
100%的数据满足: 1k15,1ci5 1 ≤ k ≤ 15 , 1 ≤ c i ≤ 5

题解

这道题看起来像一道dp题。
首先看一看数据范围,暴搜是能得50分的。再看100分的数据范围,发现 ci c i 最大只有5,是不是感觉发现了米奇妙妙屋?
然后我们就可以联想到乌龟棋,是否可以用乌龟棋的方式表示状态?

dp[a][b][c][d][e] d p [ a ] [ b ] [ c ] [ d ] [ e ] 表示还能用5次的颜色个数为 a a ,还能用4次的颜色个数为b,还能用3次的颜色个数为 c c ,还能用2次的颜色个数为d,还能用1次的颜色个数为 e e 时的方案总数,那么我们就可以得到一个状态转移方程(注意这不是最后的方程):
dp[a][b][c][d][e]=dp[a1][b+1][c][d][e]a+dp[a][b1][c+1][d][e]b+dp[a][b][c][d][e1]e

不过我们还没有考虑相邻不能重复的情况,那么就再加一维last表示上一次选的颜色在选之前还能用last次,这样每次算的时候如果要选的颜色的次数=last-1,,-1就可以了。

PS:bzoj还卡空间,把 dp[17][17][17][17][17][17] d p [ 17 ] [ 17 ] [ 17 ] [ 17 ] [ 17 ] [ 17 ] 改成 dp[16][16][16][16][16][16] d p [ 16 ] [ 16 ] [ 16 ] [ 16 ] [ 16 ] [ 16 ] 才过的。

Code

#include<bits/stdc++.h>
using namespace std;

const long long mod=1e9+7;
int k,c[20],tot[6];
long long dp[17][17][17][17][17][17];

long long dfs(int last,int a,int b,int c,int d,int e){
    long long &ret=dp[last][a][b][c][d][e];
    if(last&&ret)return ret;
    if(a)ret+=dfs(5,a-1,b+1,c,d,e)*a;
    if(b)ret+=dfs(4,a,b-1,c+1,d,e)*(b-(last==5));
    if(c)ret+=dfs(3,a,b,c-1,d+1,e)*(c-(last==4));
    if(d)ret+=dfs(2,a,b,c,d-1,e+1)*(d-(last==3));
    if(e)ret+=dfs(1,a,b,c,d,e-1)*(e-(last==2));
    return ret=ret%mod;
}

int main()
{
    scanf("%d",&k);
    for(int i=1;i<=k;i++)scanf("%d",&c[i]),tot[c[i]]++;
    dp[1][0][0][0][0][0]=1;
    printf("%lld",dfs(0,tot[5],tot[4],tot[3],tot[2],tot[1]));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值