bzoj 1079 //1079: [SCOI2008]着色方案 记忆化搜索(DP)/组合数学+DP

bzoj 1079   //1079: [SCOI2008]着色方案   记忆化搜索(DP)/组合数学+DP

bzoj 1079   //1079: [SCOI2008]着色方案   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1079
//在线测评地址https://www.luogu.org/problem/P2476

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

方法一:记忆化搜索(DP)

Accepted 49972 kb 680 ms C++/Edit 1072 B

//此文https://www.cnblogs.com/ljh2000-jump/p/5771185.html代码思路都写得不错,摘抄如下
/*
正解:记忆化搜索(DP)
解题报告:
这道题好神啊,合并操作的确很具有代表性。
如果考虑如何求方案数,不妨DP统计,因为直接枚举与转移复杂度太大。我们选择把剩余可涂次数相等的分为一类,考虑因为只要剩余可涂次数相等,那么其实这些颜色并没有什么区别,因为我们涂颜色的时候只需要考虑涂的这种颜色剩余次数即可,并不需要考虑具体是什么颜色(先不考虑相邻颜色不能相等的限制)。
那么令f[a1][a2][a3][a4][a5]表示剩余可涂一次的颜色种类数为a1,涂二次颜色种类数为a2...这样的情况的方案数。
显然如果我们当前这次选的是剩余次数为x次的颜色,那么剩余次数为x次的颜色有多少种,就有多少种情况可以转移过来,只需要乘以数量就可以了。
但是我们还没考虑相邻不能相等的情况,那么我们必须要少算一次,比如说如果上次填的是颜色剩余次数为2的,意味着颜色中剩余次数为1的多了一个,那么这一次并不能再选这种颜色,这次可以选填1的就要少1。
*/
//注意:刷漆,是自左往右,连续刷,每刷一次,做一次判断。
//样例通过,提交Runtime_Error    49968 kb    60 ms    C++/Edit    928 B。2019-11-13
//排查,发现
/*
if(a4)now+=(a4-(last==5))*dfs(a1,a2,a3+1,a4-1,a5,4),now%=mod;//此处错写成if(a4)now+=(a4-(last==5))*dfs(a1-1,a2,a3+1,a4-1,a5,4),now%=mod;
    if(a5)now+=a5*dfs(a1,a2,a3,a4+1,a5-1,5),now%=mod;//此处错写成if(a5)now+=a5*dfs(a1-1,a2,a3,a4+1,a5-1,5),now%=mod;
*/
//样例通过,提交AC.2019-11-13
#include
#include
#define mod 1000000007
#define LL long long
LL dp[16][16][16][16][16][6];//按每种颜色的剩余次数分类,剩余次数相同的分在一类
int num[6],k;
LL dfs(int a1,int a2,int a3,int a4,int a5,int last){
    LL now=0;
    if(a1+a2+a3+a4+a5==0)return 1;
    //考虑枚举这次选哪种剩余次数的颜色,一共有多少个剩余这种次数的个数就有多少种选择
    if(dp[a1][a2][a3][a4][a5][last])return dp[a1][a2][a3][a4][a5][last];//如果上次填的是颜色剩余次数为2的,意味着颜色中剩余次数为1的多了一个,那么这一次并不能再选这种颜色,这次可以选填1的就要少1。
    if(a1)now+=(a1-(last==2))*dfs(a1-1,a2,a3,a4,a5,1),now%=mod;
    if(a2)now+=(a2-(last==3))*dfs(a1+1,a2-1,a3,a4,a5,2),now%=mod;
    if(a3)now+=(a3-(last==4))*dfs(a1,a2+1,a3-1,a4,a5,3),now%=mod;
    if(a4)now+=(a4-(last==5))*dfs(a1,a2,a3+1,a4-1,a5,4),now%=mod;//此处错写成if(a4)now+=(a4-(last==5))*dfs(a1-1,a2,a3+1,a4-1,a5,4),now%=mod;
    if(a5)now+=a5*dfs(a1,a2,a3,a4+1,a5-1,5),now%=mod;//此处错写成if(a5)now+=a5*dfs(a1-1,a2,a3,a4+1,a5-1,5),now%=mod;//显然不需要考虑6的情况
    return dp[a1][a2][a3][a4][a5][last]=now;
}
int main(){
    int i,x;
    memset(dp,0,sizeof(dp)),memset(num,0,sizeof(num));
    scanf("%d",&k);
    for(i=1;i<=k;i++)scanf("%d",&x),num[x]++;
    printf("%lld\n",dfs(num[1],num[2],num[3],num[4],num[5],0));
    return 0;
}

方法二:组合数学+DP

Accepted 876 kb 40 ms C++/Edit 920 B

//此文https://www.cnblogs.com/bztMinamoto/p/9954620.html思路不错,摘抄如下

如c[1]=2,f[1][1]=1, 11只有1对11

如c[1]=3,f[1][2]=1,111有2对11|1,1|11

如c[1]=4,f[1][3]=1,1111有3对11|11,1|11|1,11|11

/*解释如下,隔板法,c[i+1]个数,可插c[i+1]-1个隔板,放入a-1个隔板,就分成了a组
具体例子如下
123456789共计9个数,可放8个隔板,1|2|3|4|5|6|7|8|9,分成3组,其中一种隔板摆法如下
1|2|3456789,只需放入2个隔板。
*/

bzoj 1079 //1079: [SCOI2008]着色方案 记忆化搜索(DP)/组合数学+DP_第1张图片

如j=4,b=2,

11111,可放4个隔板1|1|1|1|1放2个隔板的一种情况如右1|1|111

123456789共计9个数,可放10个隔板|1|2|3|4|5|6|7|8|9|,即可放隔板板的数量是sum[i]+1

理解如右:j-b因原有j对,插入b组,被破坏了b对,剩j-b对

c[i+1]中有c[i+1]-1对,被分隔成a-1组,即被破坏了了a-1对,剩(c[i+1]-1)-(a-1)=c[i+1]-a对

总共剩j-b+c[i+1]-a对。2019-11-14

//杨辉三角编写完成后,马上测试。
//for(j=0;j //样例通过,提交AC.2019-11-14
#include
#include
#define LL long long
#define mod 1000000007
int K,c[16],sum[16];
LL C[76][76],f[16][76];
void comb(){//杨辉三角
    int i,j;
    for(i=0;i<=75;i++)C[i][0]=C[i][i]=1;
    for(i=2;i<=75;i++)
        for(j=1;j<=i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
int main(){
    int i,j,a,b;
    comb(),memset(f,0,sizeof(f));
    scanf("%d",&K),sum[0]=0;
    for(i=1;i<=K;i++)scanf("%d",&c[i]),sum[i]=sum[i-1]+c[i];
    f[1][c[1]-1]=1;
    for(i=1;i         for(j=0;j             if(!f[i][j])continue;
            for(a=1;a<=c[i+1];a++)
                for(b=0;b<=a&&b<=j;b++){
                    LL t;
                    t=f[i][j]*C[c[i+1]-1][a-1]%mod;
                    t=t*C[j][b]%mod;
                    t=t*C[sum[i]+1-j][a-b]%mod;
                    f[i+1][j-b+c[i+1]-a]=(f[i+1][j-b+c[i+1]-a]+t)%mod;
                }
        }
    printf("%lld\n",f[K][0]);
    return 0;
}

你可能感兴趣的:(跟着大佬学算法)