[BZOJ1079]-[SCOI2008]着色方案-迷之DP

说在前面

这DP的状态十分神奇…
强迫症如me,不过代码对齐了真的很好看!


题目

BZOJ1079传送门

题面

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

输入和输出

输入:第一行为一个正整数k,第二行包含k个整数c1, c2, … , ck。
输出:一个整数,即方案总数模1,000,000,007的结果。

数据范围

K≤15,所有ci≤5


解法

观察到这个K和ci都十分的小,一开始以为是搜索。但是如果所有颜色的ci都是5的话,那么n最大还是能达到75,而这个大小明显是不能用搜索写的。
可以发现,颜色不同其实对于我们的决策没有影响(反正个数一样,下一块我选哪种颜色涂都是一样的)。于是把相同颜色的颜色压在一起,搞记忆化搜索。

定义dp[x1][x2][x3][x4][x5][las]表示,当前剩余次数为i的颜色有xi种,上一次选的是剩下了las次数的颜色。
因为剩余了i次的颜色不止一种,所以转移的时候乘上xi。注意因为不能涂相同颜色,因此las对应的那个xi要减1。细节看代码。


自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mgg dp[x1][x2][x3][x4][x5][las]
using namespace std ;

int K , c[20] , s[6] , mmod = 1000000007 ;
int dp[16][16][16][16][16][6] ;

int dfs( int x1, int x2, int x3, int x4, int x5, int las ){
    if( !(x1|x2|x3|x4|x5) ) return 1 ;
    if( mgg != -1 ) return mgg ;
    long long rt = 0 ;
    if( x1 ) rt += 1LL * ( x1 - ( las == 2 ) ) * dfs( x1-1 , x2   , x3   , x4   , x5   , 1 ) ;
    if( x2 ) rt += 1LL * ( x2 - ( las == 3 ) ) * dfs( x1+1 , x2-1 , x3   , x4   , x5   , 2 ) ;
    if( x3 ) rt += 1LL * ( x3 - ( las == 4 ) ) * dfs( x1   , x2+1 , x3-1 , x4   , x5   , 3 ) ;
    if( x4 ) rt += 1LL * ( x4 - ( las == 5 ) ) * dfs( x1   , x2   , x3+1 , x4-1 , x5   , 4 ) ;
    if( x5 ) rt += 1LL * ( x5                ) * dfs( x1   , x2   , x3   , x4+1 , x5-1 , 5 ) ;
    return dp[x1][x2][x3][x4][x5][las] = ( rt%mmod ) ;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值