#HDU4248#A Famous Stone Collector(组合数)

27 篇文章 0 订阅
14 篇文章 0 订阅

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.
 
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.

定义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
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;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值