A Famous Stone Collector
Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1385 Accepted Submission(s): 534
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.
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.
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.
which is a prime number.
Sample Input
3 1 1 1 2 1 2
Sample Output
Case 1: 15 Case 2: 8HintIn 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.
定义Dp[i][j]表示前i种石头,摆出长度为j的图案的方案数
Dp[i][j] = Dp[i - 1][j] + Dp[i - 1][j - k] * C[j][k]
第i种石头不用的方案数 + 用k个第i种石头的方案数
在理解后面第i种石头要放的时候,这里给出两种理解思路:
第一种很粗暴简单:
在长度为 j 的序列中选出 k 个放第 i 种石头 => C[ j ][ k ]
第二种要绕一点,不过也很经典,借助第二类stirling:
在之前的 j - k 个石头中,每个石头的左边或右边都会有空位可以放,
这种空位一共有 j - k + 1 个(包括最左边和最右边),
现在要将第 i 种的 k 个石头放入这 j - k + 1 个空位中,
根据第二类Stirling数(详细证明及解释见:http://blog.csdn.net/its_elaine/article/details/54973630)
所以有 C[ (j - k + 1) + k - 1 ][ j - k + 1 - 1 ] = C[ j ][ j - k ] = C[ j ][ k ]
Code:
Status | Accepted |
---|---|
Time | 2480ms |
Memory | 18120kB |
Length | 947 |
Lang | G++ |
Submitted | 2017-07-08 09:32:17 |
Shared | |
RemoteRunId | 21023973 |
#include<iostream>//Dp[i][j]:前i种石头摆放成长度为j的图案的方案数
#include<cstdio>//考虑第i种石头,可以放,可以不放。若不放,则Dp[i - 1][j]前i - 1种石头摆放成长度为j
#include<cstdlib>//若放,考虑放的个数,设第i种石头放了k个,则Dp[i - 1][j - k]为之前的石头的方案数
#include<cstring>//这k个石头放的位置不同将会产生新的方案,从j个不同的位置里面选k个位置放第i种石头
using namespace std;//也可以看成在之前的j - k个石头的每一个石头的左边或右边都可以放,那么就有j - k + 1个位置可以放,于是将k个石头分进j - k + 1个位置中
//变成C[(j - k + 1) + k - 1][(j - k + 1) - 1] = C[j][j - k] = C[j][k]
typedef long long LL;
const int MOD = 1000000007;
int A[105];
LL C[10005][105], Dp[105][10005];
void pre_work(){
C[0][0] = 1LL;
for(int i = 1; i <= 10000; ++ i){
C[i][0] = 1LL;//一定要注意,我之前的习惯是赋成C[i][0] = C[i][i] = 1LL,但是这里这样赋会超出数组范围,考试就是这里错了。
for(int j = 1; j <= i && j <= 100; ++ j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
int main(){
pre_work();
int N, tot = 0;
while(~scanf("%d", &N)){
for(int i = 1; i <= N; ++i)
scanf("%d", &A[i]);
memset(Dp, 0, sizeof(Dp));
Dp[0][0] = 1;
LL summax = 0LL;
for(int i = 1; i <= N; ++ i){
summax += A[i];
for(int j = 0; j <= summax; ++ j){
Dp[i][j] = Dp[i - 1][j];
for(int k = 1; k <= A[i] && k <= j; ++ k)
Dp[i][j] = (Dp[i][j] + Dp[i - 1][j - k] * C[j][k] % MOD) % MOD;
}
}
LL Ans = 0LL;
for(int i = 1; i <= summax; ++ i)
Ans = (Ans + Dp[N][i]) % MOD;
printf("Case %d: %I64d\n", ++ tot, Ans);
}
return 0;
}