BZOJ1079 [SCOI2008]着色方案 动态规划

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1079


题目概括

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


 

题解

  一开始想状压dp,压每种颜色的剩余数。

  发现要超时。

  访问了hzwer大佬的博客,立刻恍然大悟。

  我们可以压每种剩余数的颜色个数!

  具体:

  dp[a][b][c][d][e][t]表示剩余1的颜色有a个,剩余2的颜色有b个,剩余3的颜色有c个,剩余4的颜色有d个,剩余5的颜色有e个,之前选择的那种颜色现在还剩t的方案总数。

  那么复杂度为165×6≈6500000,应该不会超时了。

  记忆化dfs比较好写,所以写了记忆化dfs。


 

代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long LL;
const LL mod=1000000007;
int k,tot[6];
LL dp[16][16][16][16][16][6];
LL DP(int a,int b,int c,int d,int e,int t){
    if (dp[a][b][c][d][e][t]!=-1)
        return dp[a][b][c][d][e][t];
    if (a+b+c+d+e==0)
        return dp[a][b][c][d][e][t]=1;
    int A=a-(t==1),B=b-(t==2),C=c-(t==3),D=d-(t==4),E=e;
    LL &res=dp[a][b][c][d][e][t];
    res=0;
    if (a)
        res+=A*DP(a-1,b,c,d,e,0);
    if (b)
        res+=B*DP(a+1,b-1,c,d,e,1);
    if (c)
        res+=C*DP(a,b+1,c-1,d,e,2);
    if (d)
        res+=D*DP(a,b,c+1,d-1,e,3);
    if (e)
        res+=E*DP(a,b,c,d+1,e-1,4);
    return res%=mod;
}
int main(){
    memset(dp,-1,sizeof dp);
    memset(tot,0,sizeof tot);
    scanf("%d",&k);
    for (int i=1,a;i<=k;i++){
        scanf("%d",&a);
        tot[a]++;
    }
    printf("%lld",DP(tot[1],tot[2],tot[3],tot[4],tot[5],0));
    return 0;
}

 

posted @ 2017-08-16 17:17  zzd233  阅读(239)  评论(0编辑  收藏  举报