A Famous Stone Collector



Problem Description
Mr. B loves to play with colorful stones. There are n colors of stones in his collection. Two stones with the same color are indistinguishable. Mr. B would like to 
select some stones and arrange them in line to form a beautiful pattern. After several arrangements he finds it very hard for him to enumerate all the patterns. So he asks you to write a program to count the number of different possible patterns. Two patterns are considered different, if and only if they have different number of stones or have different colors on at least one position.
 

Input
Each test case starts with a line containing an integer n indicating the kinds of stones Mr. B have. Following this is a line containing n integers - the number of 
available stones of each color respectively. All the input numbers will be nonnegative and no more than 100.
 

Output
For each test case, display a single line containing the case number and the number of different patterns Mr. B can make with these stones, modulo 1,000,000,007, 
which is a prime number.
 

Sample Input
3 1 1 1 2 1 2
 

Sample Output
Case 1: 15 Case 2: 8
Hint
In the first case, suppose the colors of the stones Mr. B has are B, G and M, the different patterns Mr. B can form are: B; G; M; BG; BM; GM; GB; MB; MG; BGM; BMG; GBM; GMB; MBG; MGB.
 

Source


题意为:

有n种颜色的石头,每种颜色有num[i]块,每种颜色的石头是不可区分的,在这么多石头里面挑选石头来排成一条线,问一共同拥有多少种排法。线的长度为(1到  num[i]+num[2]+num[3]+.....num[n]。

比方有三种颜色石头,B,G,M,表示颜色。每种颜色一块石头,那么可能的序列有 B; G; M; BG; BM; GM; GB; MB; MG; BGM; BMG; GBM; GMB; MBG; MGB

用dp[i][j]代表用前i种颜色的石头去排成长度为j的序列

那么有那种情况:

①第i种颜色的石头不使用,那么 dp[i][j] = dp[i-1][j];

②第i种颜色的石头使用(序列可到达长度为原来的长度加上第i种颜色的个数),有num[i]个。原来的石头序列中去掉k个,从第i种颜色石头中取出k个。这里(k<=j),插入到原来的序列中,一共同拥有j个位置,从中挑选k个就能够了(不是单纯的插空。能够理解为,要构成长度j的序列,有j个位置,首先让第i种颜色的k个石头去挑k个位置。这就包含了相邻和不相邻的情况,因为同样颜色不可区分,有 c[j][k]种方法,然后再让剩下的石头序列依照原来的顺序依次填入每一个空位置就能够了).

有dp[i][j]+=dp[i-1][j-k]*c[j][k];

代码:

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll;
const int maxn=110;//种类数最大
const int mod=1000000007;
ll dp[maxn][maxn*maxn];//代表用前i种颜色组成长度为j的序列的方法数
ll c[maxn*maxn][maxn];//从当前序列长度+1种中选出j种。j不会超过maxn
int n;//n种颜色
int num[maxn];//每种颜色有多少石头
int sum;//最长的序列长度

void getC()
{
    c[0][0]=c[1][0]=c[1][1]=1;
    for(int i=2;i<maxn*maxn-1;i++)
    {
        c[i][0]=1;
        for(int j=1;j<maxn-1;j++)
        {
            if(i==j)
                c[i][j]=1;
            else
            {
                c[i][j]=c[i-1][j]+c[i-1][j-1];
                c[i][j]%=mod;
            }
        }
    }
}

int DP()
{
    memset(dp,0,sizeof(dp));//一定的初始化为0。后面dp[i][j]=dp[i-1][j]用到了..
    for(int i=1;i<=n;i++)
        dp[i][0]=1;
    for(int i=1;i<=num[1];i++)
        dp[1][i]=1;
    int tsum=num[1];
    for(int i=2;i<=n;i++)//一共同拥有N种
    {
        tsum+=num[i];//前i种颜色的总长度,最多的
        for(int j=1;j<=tsum;j++)
        {
            dp[i][j]=dp[i-1][j];//对于当前长度j,一种情况是不用第i种颜色的石头
            for(int k=1;k<=num[i]&&k<=j;k++)//使用当前颜色石头,可是要去掉一定的长度,使用新的颜色石头来填充
            {
                dp[i][j]+=dp[i-1][j-k]*c[j][k];//为什么是 c[j][k]呢,能够这样理解。一共同拥有J个位置。让第i中颜色的求先去选当中k个位置。然后剩下的位置让原来颜色的顺序放下就好了
                dp[i][j]%=mod;
            }
        }
    }
    ll ans=0;
    for(int i=1;i<=sum;i++)
    {
        ans+=dp[n][i];
        ans%=mod;
    }
    return ans;
}

int main()
{
    getC();
    int cas=1;
    while(cin>>n)
    {
        sum=0;
        for(int i=1;i<=n;i++)
        {
            cin>>num[i];
            sum+=num[i];
        }
        cout<<"Case "<<cas++<<": "<<DP()<<endl;
    }
    return 0;
}



posted on 2017-05-31 15:18  yutingliuyl  阅读(300)  评论(0编辑  收藏  举报