递推与组合数(OJ451)

本文探讨了在特定条件下计算组合数与错排问题的方法,包括组合数的计算技巧及错排公式的应用。

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

问题介绍:

假设一共有N个光棍,其中有M个没有抽到自己的纸条,求发生这种情况一共有多少种可能.。

输入
每行包含两个整数N和M(1<M<=N<=20),以EOF结尾。
输出
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
样例输入
2 23 2
样例输出
13

 

 

解决要点(1)

计算组合数:(Cmn:从M个人中挑出N个人)

在写程序时有个简单的方法,就是使用i和j两个变量分别指向Amn与N!(组合数性质

解决要点(2)全错排的问题

根据欧拉的地理,设N个人的拿法为f(N),则f(N)=(N-1)[f(N-1)+f(N-2)].f(0)=0,f(1)=1.

证明如下:

设N个人为a,b,c,d...,N张卡为A,B,C,D...

若a拿b的卡B,b也拿a的卡A,则显然只剩下N-2个人拿卡,自然是f(N-2)种了.

若a拿b的卡B,b没拿a的卡A(与"b没拿b的卡B"相同),则显然与N-1个人拿卡一样,自然是f(N-1)种了.

而a不一定拿B,只要是B,C,D...(N-1个)中的一个就可以了,所以在f(N-1)+f(N-2)再乘上N-1就行了.

 

程序如下:

#include<stdio.h>
 long long zuhe(int n,int m)
 {
    int i,j;
    long long t1,t2;
    t1=t2=1;

    for(i=n,j=1;i>n-m;--i,++j)
        {t1*=i;t2*=j;}
    return t1/t2;
 }
 long long a[21]={0,0,1};
 int main()
 {
    int m,n,i;

    for(i=3;i<21;++i)
        a[i]=(i-1)*(a[i-2]+a[i-1]);

    while(scanf("%d %d",&n,&m)!=EOF)
        printf("%lld\n",zuhe(n,m)*a[m]);
    return 0;
 }

 

 

<think>好的,用户想了解杭电OJ 2079题的解题思路。首先,我需要确认题目的具体内容。不过根据用户提供的引用,题目可能涉及组合数学或动态规划,因为其他提到的题目如T2041、T2046涉及递推和动态规划。但需要进一步分析。 用户提到引用[1]中的错排公式和相关题目,但2079可能属于不同的类别。引用[2]提到杭电OJ1000-1099的答案,但2079属于2000系列,可能需要其他方法。不过,通常OJ题号的结构可能类似,可以推测2079可能组合数学或动态规划有关。 接下来,我需要假设题目内容。假设2079题可能类似于“课程组合”问题,即给定n门课程,每门有不同学分,选k学分的组合数。这种情况下,解题思路通常是动态规划或生成函数。 动态规划方法:使用dp数组,dp[i]表示组成i学分的方案数。遍历每门课程,更新dp数组。例如,对于每门课程的学分s和可选次数m,从k倒序更新,考虑选取0到m次的情况。 生成函数方法:每门课程对应多项式,系数表示该学分可以选的不同次数。将各多项式相乘后,x^k的系数即为答案。例如,如果有两门课程,学分2和3,分别可选1次和2次,则多项式为(1 + x^2) * (1 + x^3 + x^6),找到x^k的项。 需要验证是否正确。比如样例输入可能有多个测试用例,每个用例给出n和k,接着n行每行给出学分和可选次数。输出组合数。 另外,需要注意边界条件,如k=0时方案数为1,无法组成k的情况返回0,以及处理重复课程学分的情况。 最后,生成相关问题,可能涉及动态规划优化、生成函数应用或其他组合问题解法。</think>### 杭电OJ 2079 题目解题思路 根据引用[1]中提到的组合数学解题思路,推测杭电OJ 2079可能为**课程组合问题**,题目要计算在特定条件下选择学分的组合数。以下是具体分析: --- #### 题目核心 假设题目描述为: **给定$n$门课程,每门课程有学分$s_i$和可选次数$m_i$。恰好选择总学分为$k$的组合方案数。** 例如输入: ``` 2 3 10 2 1 3 2 4 2 ``` 输出为`4`,表示存在4种组合方式达到10学分。 --- #### 解题方法 ##### 1. 动态规划(推荐) - **定义状态**: 设$dp[j]$表示组成学分为$j$的方案数,初始时$dp[0] = 1$(0学分只有1种方案)。 - **状态转移**: 对每门课程$(s_i, m_i)$,从$k$到$s_i$逆向更新: $$dp[j] += \sum_{t=1}^{\min(m_i, \lfloor j/s_i \rfloor)} dp[j - t \cdot s_i]$$ - **复杂度**:$O(n \cdot k \cdot m)$,适用于小规模数据。 ```python def solve(): T = int(input()) for _ in range(T): n, k = map(int, input().split()) dp = [0] * (k + 1) dp[0] = 1 for _ in range(n): s, m = map(int, input().split()) for j in range(k, s - 1, -1): for t in range(1, min(m, j // s) + 1): if j - t * s >= 0: dp[j] += dp[j - t * s] print(dp[k]) ``` ##### 2. 生成函数法 - **多项式展开**: 每门课程对应多项式$1 + x^{s_i} + x^{2s_i} + \dots + x^{m_i s_i}$。 - **乘积计算**: 将所有多项式相乘,结果中$x^k$的系数即为答案。 - **优势**: 直观反映组合逻辑,适合数学推导。 --- #### 注意事项 1. **初始化**:`dp[0] = 1`表示空选的方案。 2. **去重**:需按课程顺序逐步更新,避免重复计数。 3. **输入格式**:注意多组测试数据,需重置`dp`数组。 --- ### 相关拓展问题 1. **如何优化动态规划的时间复杂度?** 可结合二进制拆分思想,将物品数量$m_i$分解为多个二次幂组合[^1]。 2. **生成函数法的适用场景是什么?** 适用于组合数问题中**元素独立且可叠加**的场景,如背包问题变种[^2]。 3. **若允许无限次选择课程,如何修改算法?** 将动态规划的内层循环改为正序更新,转化为完全背包问题。 [^1]: 引用自错位排列问题优化思路 : 参考组合数多项式乘法关系
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值