【HDU 4248】组合数学 A Famous Stone Collector

来源:互联网 发布:php app接口开发步骤 编辑:程序博客网 时间:2024/06/05 09:43

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
31 1 121 2
Sample Output
Case 1: 15Case 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.

    题目大意:

    有n种不同颜色的石头,每种颜色有ki块,同色石头全都一样,现在将它们选出若干块任意排列,让你求共有多少种不同的排列方式。


    既然是组合数学的诈尸二连击,还是应该纪念一下!

    这道题初看时我毫无头绪,想起可重复元素的全排列公式:S=n!/(n1!*n2!*...*nm!),可是却没有一个选排列公式,若是枚举用哪些石头,最高可达2^10000种方法,无奈只能放弃这个做法。后来我发现,如果使用类似于动态规划的方法,进行分步计数,或许就没有那么复杂了,递推式也很好想:

    设f[i][j]表示用前i种石头摆j个的方法,则f[i][j]=sum(f[i-1][k]*C(j,k)),其中max(j-stone[i],0)≤kj,可以发现这是一个三维的,i有100,j有10000,k有100,仔细观察时间限制是15s,这样是完全可以过的。最后输出f[n][i](1i≤Σstone[i])即可。其中stone表示第i种石头个数。初始化f[i][0]=1并刷出组合数表。

    还是来解释一下状态转移方程式,由于前i种石头摆了j个,那么第i种石头可以摆0~stone[i]个,然后剩下的就是前i-1种石头了。而对于前i-1种石头每一种方案,第i种石头都可以有C(j,k)种方式摆放。为什么是C(j,k)呢?我们把这j块石头看做一个序列OOO...OOO,其中有j-k块是第i种,从这j块石头中选出j-k块放i即可,就是C(j,j-k),也就等于C(j,k)(这样看着清爽...)

    所以我就这么写了,结果RE,原因是我的组合数表只打到了C(10000,100),但我写了C(j,k)然后k可以大于100!所以还是写成C(j,j-k)吧555。

    你们要的代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>using namespace std;#define MOD 1000000007#define ll long longint n;ll C[10005][105],stone[105],S[105],f[105][10005];int main(){C[0][0]=1;for(int i=1;i<=10000;i++){C[i][0]=1;for(int j=1;j<=i&&j<=100;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;}for(int cas=1;scanf("%d",&n)==1;cas++){memset(f,0,sizeof(f));for(int i=1;i<=n;i++){scanf("%lld",&stone[i]);S[i]=stone[i]+S[i-1];}f[0][0]=1;for(int i=1;i<=n;i++){f[i][0]=1;for(int j=1;j<=S[i];j++)for(int k=j;k>=0&&k>=j-stone[i];k--)f[i][j]=(f[i][j]+(f[i-1][k]*C[j][j-k])%MOD)%MOD;}ll ans=0;for(int i=1;i<=S[n];i++)ans=(ans+f[n][i])%MOD;printf("Case %d: %lld\n",cas,ans);}}
原创粉丝点击