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

本文介绍了一道关于排列组合的问题,即如何计算使用不同颜色石头摆放出的所有可能图案的数量,并提供了一个具体的算法实现,包括状态转移方程和组合数的预处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A Famous Stone Collector

题目

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.

翻译

Mr.B有n种颜色的石头,每种颜色的石头有A[i]个,用石头摆图案,求图案的方案数。
其实就相当于有n种颜色的小球,每种颜色有一定的数量,小球除了颜色没有区别,求方案数。

分析

状态转移方程式:
dp[i][j]//表示用到第i种颜色,用了j块石头
=dp[i-1][j-k]*C[j][k](0<=k<=min(A[i],j))

我们枚举k,表示我们放了k个i颜色石头,
所以说,前面i-1个颜色就占了j-k个位置,而这么放的方案数已知,因此要加上dp[i-1][j-k],
还有k个位置,这k个位置有多少种情况呢?就是C(n,k)//C是组合

预处理组合数就行了,最后注意精度、取模以及多组数据。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
#define mod 1000000007

ll C[10005][105],dp[105][10005];
void Sc()
{
    C[0][0]=1LL;
    for(int i=1;i<=10000;i++)
    {
        C[i][0]=1LL;
        for(int j=1;j<=100&&j<=i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
}

int n,A[105];

int main()
{
    Sc();
    int cr=0;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",&A[i]);
        ll sig=0LL;
        memset(dp,0,sizeof dp);
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            sig+=A[i];
            for(ll j=0;j<=sig;j++)
                for(ll k=0;k<=A[i]&&k<=j;k++)
                    dp[i][j]=(dp[i][j]+dp[i-1][j-k]%mod*C[j][k]%mod)%mod;
        }
        ll ans=0LL;
        for(ll i=1;i<=sig;i++)
            ans+=dp[n][i],ans%=mod;
        printf("Case %d: ",++cr);
        cout<<ans<<endl;
    }
}
### HDU OJ 排列组合问题解法 排列组合问题是算法竞赛中的常见题型之一,涉及数学基础以及高效的实现技巧。以下是关于如何解决此类问题的一些通用方法和具体实例。 #### 数学基础知识 在处理排列组合问题时,需要熟悉以下几个基本概念: - **阶乘计算**:用于求解全排列的数量 $ n! = n \times (n-1) \times ... \times 1 $[^4]。 - **组合数公式**:$ C(n, k) = \frac{n!}{k!(n-k)!} $ 表示从 $ n $ 中选取 $ k $ 的方案数[^5]。 - **快速幂运算**:当涉及到模运算时,可以利用费马小定理优化逆元的计算[^6]。 #### 题目推荐与分析 以下是一些典型的 HDU OJ 上的排列组合题目及其可能的解法: ##### 1. 基础排列组合计数 - **HDU 2039 近似数** - 描述:给定两个整数 $ a $ 和 $ b $,统计区间内的近似数数量。 - 方法:通过枚举每一位上的可能性来构建合法数字并计数[^7]。 ```cpp #include <iostream> using namespace std; long long comb(int n, int r){ if(r > n || r < 0)return 0; long long res=1; for(int i=1;i<=r;i++)res=res*(n-i+1)/i; return res; } int main(){ int t,n,k; cin>>t; while(t--){ cin>>n>>k; cout<<comb(n+k-1,k)<<endl; // 组合数应用 } } ``` ##### 2. 动态规划的应用 - **HDU 1028 Ignatius and the Princess III** - 描述:给出正整数 $ m $ 和 $ n $,问有多少种方式把 $ m $ 分成最多 $ n $ 份。 - 方法:定义状态转移方程 $ dp[i][j]=dp[i-1][j]+dp[i][j-i] $ 来表示当前总和为 $ j $ 并分成至多 $ i $ 份的情况数目[^8]。 ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN=1e3+5; long long c[MAXN][MAXN]; void init(){ memset(c,0,sizeof(c)); c[0][0]=1; for(int i=1;i<MAXN;i++){ c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%(1e9+7); } } int main(){ init(); int T,m,n; scanf("%d",&T); while(T--){ scanf("%d%d",&m,&n); printf("%lld\n",c[m+n-1][min(m,n)]); } } ``` #### 总结 针对不同类型的排列组合问题,可以选择合适的工具和技术加以应对。无论是简单的直接计算还是复杂的动态规划模型,都需要扎实的基础知识作为支撑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值