hdu 湫秋系列故事——安排座位(组合dp)

/*
 * 以前比赛的时候做这题,一点思路都没有,后面就放了放;
 * 
 * 今天突然想来弄下这题,于是去拜访了大牛的博客,了解到了组合dp的解法
 * 
 * 解法如下: 首先我们令F[i][j]表示前i个系的所有学生坐在座位上,出现了j对相邻学生是同一个系的
 * 
 * 那么对于接下来一个系的a个学生,我们可以把这个系的学生插入前面出现的j对相邻学生的中间,从而使他们变得不相邻
 * 
 * 现在的问题在于怎么去插入,我们可以采用分组的方法,我们把这个系的a个学生分成k组,从k组中选g组插入前面的j对相邻的
 * 
 * 学生中间,其他的组插入其他的空格内,则插入的方案数为A(a,a)*C(j,g)*C(sum+1-j,k-g)*C(a-1,k-1)(sum为前i个系的总的学生,
 * 
 * A(a,a)表示学生的位置全排,C(j,g)表示j个空插入g组,C(sum-1-j,k-g)表示剩下的空插入剩下的组,C(a-1,k-1)表示用隔板法把a个学生分成k组)
 * 
 * 因此转移方程为F[i][j-g+a-k]+=F[i-1][j]*A(a,a)*C(j,g)*C(sum+1-j,k-g)*C(a-1,k-1)且F[0][0]=1;
 * 
 *

 */


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define mod 1000000007

long long F[50][500],C[510][510],A[55];
long long dfs(int n,int m)
{
    if(n<m) return 0;
    if(m==0||n==m) return 1;
    if(C[n][m]!=-1) return C[n][m];
    return C[n][m]=(dfs(n-1,m-1)+dfs(n-1,m))%mod;
}
int main(){
    int cas,n,k,i,g,j,a,sum,t=1;
    memset(C,-1,sizeof(C));A[0]=1;
    for(i=1;i<=50;i++)  A[i]=(A[i-1]*i)%mod;
    for(i=0;i<=500;i++) for(j=0;j<=500;j++) C[i][j]=dfs(i,j);
    scanf("%d",&cas);
    while(cas--){
        scanf("%d",&n);
        memset(F,0,sizeof(F));
        F[0][0]=1;sum=0;
        for(i=1;i<=n;i++){
            scanf("%d",&a);
            for(j=0;j<=sum;j++){
                for(k=1;k<=a;k++){
                    for(g=0;g<=j&&g<=k;g++){
                        F[i][j-g+a-k]+=(((F[i-1][j]*C[a-1][k-1])%mod*C[j][g])%mod*C[sum+1-j][k-g])%mod*A[a]%mod;
                        F[i][j-g+a-k]%=mod;
                    }
                }
            }
            sum+=a;
        }
        printf("Case %d: %d\n",t++,int(F[n][0]));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值