组合数+动态规划

1:求相同班级的学生不相邻的全排列

f[i][j]代表已经处理完了前i个班级,有多少个空隙左边和右边的同学的班级相同。

我们考虑把第i个班级的同学分成k组,然后有u组分在了左边和右边相同的空隙中,其他的分在了左边和右边不相同的空隙中。

首先把把a[i]个学生分成k组,所以这个分法一共有C[a[i]-1][k-1];

然后把这k组同学分成u组,所以一共的分法是C[k][u];//这个东西是不能加的,想一下小球的问题就行了。

然后我把这u组放在j个空隙中,所有的可能性 C[j][u];

然后我把剩下的k-u组放在剩下的空隙中所有的可能性C[sum+1-j][k-u];

所以这个时候剩下的使左边和右边相同的空隙 j-u+b[i]-k;这个画个图就能很好的看出来了。

然后最后的结果就是f[n][0]然后在让所有的同学不同就行了,然后就是乘每个班级人数的全排列就行了。

#include<bits/stdc++.h>

using namespace std;
const int MOD=1e9+7;
long long C[699][600];
long long dp[50][500];
long long fac[600];
int a[600];
void inist()
{
    memset(C,0,sizeof(C));
    for(int i=0;i<=500;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
        }
    }
    fac[0]=1;
    for(int i=1;i<=500;i++) fac[i]=fac[i-1]*i%MOD;
}
int main()
{
    inist();
    int t,n;
    scanf("%d",&t);
    int id=0;
    while(t--)
    {
        id++;
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        dp[1][a[1]-1]=1;
       // sum=a[1];
        int sum=0;
        for(int i=2;i<=n;i++)
        {
            sum+=a[i-1];
            for(int j=0;j<sum;j++)
            {
                if(!dp[i-1][j]) continue;
                for(int k=1;k<=a[i];k++)
                {
                    for(int u=0;u<=k&&u<=j;u++)
                    {
                        long long temp=dp[i-1][j]%MOD;
                        temp=temp*C[a[i]-1][k-1]%MOD;
                        //temp*=C[k][u]%MOD; 想一下小球放在盒子里
                        temp=temp*C[j][u]%MOD;
                        temp=temp*C[sum+1-j][k-u]%MOD;
                        dp[i][j-u+a[i]-k]=(dp[i][j-u+a[i]-k]+temp)%MOD;
                    }
                }
            }
        }
        long long ans=dp[n][0];

        for(int i=1;i<=n;i++)
        {
            ans=ans*fac[a[i]]%MOD;
        }
        printf("Case %d: ",id);
        printf("%lld\n",(ans%MOD+MOD)%MOD);
    }
}

  

posted @ 2017-08-21 18:31  Heilce  阅读(637)  评论(0编辑  收藏  举报