UVa 10943 How do you add? (组合数学)

本文介绍了一道关于将数字N分解为K个非负整数的组合数学问题,并通过编程手段进行解决。采用小球模型将问题转化为N个球放入K个盒子的不同方式数量计算,给出了解决方案并附带了C语言实现代码。

10943 - How do you add?

Time limit: 3.000 seconds

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=115&page=show_problem&problem=1884

Larry is very bad at math - he usually uses a calculator, which worked well throughout college. Unforunately, he is now struck in a deserted island with his good buddy Ryan after a snowboarding accident. They're now trying to spend some time figuring out some good problems, and Ryan will eat Larry if he cannot answer, so his fate is up to you!

It's a very simple problem - given a number N, how many ways can Knumbers less than N add up to N?

For example, for N = 20 and K = 2, there are 21 ways:
0+20
1+19
2+18
3+17
4+16
5+15
...
18+2
19+1
20+0


Input

Each line will contain a pair of numbers  N  and  K N  and  K  will both be an integer from 1 to 100, inclusive. The input will terminate on 2 0's.

Output

Since Larry is only interested in the last few digits of the answer, for each pair of numbers  N  and  K , print a single number mod 1,000,000 on a single line. 

Sample Input

20 2
20 2
0 0

Sample Output

21
21

首先题目的描述并不完整,本意是求:把N分解成K个非负整数有多少种分法。


思路:

1.  如何转化这一问题?——小球模型

这个问题可以等效成有N个相同的小球放到K个不同的盒子里,每个盒子可以为空,求一共多少种放置的方法。

2. 用何种方法求解?

可以这样理解, 我们把N个球用细线连成一排,再用K-1把刀去砍断细线,就可以把N个球按顺序分为K组(即分装到K个盒子中)。则N个球装入K个盒子的每一种装法都对应一种砍线的方法。而砍线的方法等于N个球与K-1把刀的排列方式。(注意可以让多把刀砍一根线)

故排列方法共有C(N+K-1,K-1)=C(N+K-1,N)种。

3. 如何计算?

由于要取模的原因,利用加法公式C(n+1,k+1)=C(n,k)+C(n,k+1)可以通过类似DP的递推形式求得结果。


完整代码:

/*0.012s*/

#include <cstdio>
using namespace std;
const int mod = 1000000;

int dp[101][101];

int main(void)
{
	int i, j, n, k;
	for (i = 1; i <= 100; i++)
		dp[1][i] = 1;
	for (j = 2; j <= 100; j++)
	{
	    dp[j][1] = j;
		for (i = 2; i <= 100; i++)
            dp[j][i] = (dp[j - 1][i] + dp[j][i - 1]) % mod;///公式做了一些变形
	}
	while (scanf("%d%d", &n, &k), n)
		printf("%d\n", dp[k][n]);
	return 0;
}


### 问题分析 UVA11076 - **Add Again** 是一道关于排列组合数学期望的题目。其核心任务是,给定一组整数(可能包含重复元素),计算出所有不同排列所组成的数字的总和。由于直接枚举所有排列的方式在大规模输入时效率极低,因此需要采用数学方法优化。 关键点包括: - 每个数字在每一位上出现的次数是相同的。 - 需要考虑重复元素的影响,使用多重集合的排列公式。 - 每一位的权重为 $10^i$,其中 $i$ 从右往左递增(即个位为 $10^0$,十位为 $10^1$ 等)。 ### 解题思路 1. **统计频率**:首先对输入数组中的每个数字进行计数。 2. **计算全排列数量**:利用阶乘和重复元素的除法公式: $$ \text{total\_permutations} = \frac{n!}{c_1! \cdot c_2! \cdot \cdots \cdot c_k!} $$ 其中 $n$ 是数组长度,$c_i$ 是第 $i$ 个数字的出现次数。 3. **计算每位贡献**:对于每个数字,它在整个排列中会在每一位上平均分布。因此,可以计算该数字在每一位上的总贡献,并乘以对应的 $10^i$ 权重。 4. **累加结果**:将每一位的总贡献相加,得到最终结果。 ### 示例代码实现 以下是一个 Python 实现示例,用于解决 UVA11076 问题: ```python import sys import math from collections import Counter def solve(): input = sys.stdin.read data = input().split() n = int(data[0]) arr = list(map(int, data[1:n+1])) freq = Counter(arr) # 计算全排列数量 (考虑重复元素) total_perms = math.factorial(n) for count in freq.values(): total_perms //= math.factorial(count) # 计算每一位的权重 power_of_ten = [10**i for i in range(n)] # 计算每个数字在每一位上的总贡献 result = 0 for digit, count in freq.items(): contribution_per_digit = (count * total_perms) // n # 每个数字在每一位上出现的次数 sum_of_positions = sum(power_of_ten) # 所有位置的权重之和 result += digit * contribution_per_digit * sum_of_positions print(result) if __name__ == "__main__": solve() ``` ### 复杂度分析 - 时间复杂度:主要操作为阶乘计算和遍历数字贡献,整体复杂度为 $O(n)$。 - 空间复杂度:使用额外空间存储频率和权重,整体为 $O(n)$。 ### 注意事项 - 阶乘计算可能会导致大数溢出,在实际比赛中建议使用高精度库或取模处理。 - 对于重复元素的处理,必须确保分母正确计算,否则会导致错误。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值