题解:洛谷 P1287 盒子与球
【题目来源】
【题目描述】
现有 \(r\) 个互不相同的盒子和 \(n\) 个互不相同的球,要将这 \(n\) 个球放入 \(r\) 个盒子中,且不允许有空盒子。请求出有多少种不同的放法。
两种放法不同当且仅当存在一个球使得该球在两种放法中放入了不同的盒子。
【输入】
输入只有一行两个整数,分别代表 \(n\) 和 \(r\)。
【输出】
输出一行一个整数代表答案。
【输入样例】
3 2
【输出样例】
6
【算法标签】
《洛谷 P1287 盒子与球》 #搜索# #枚举# #容斥原理# #Stirling数#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, r, f[50][50], ans; // n: 元素总数,r: 集合/盒子数量,f: DP数组,ans: 最终结果
int main()
{
// 读入n和r
cin >> n >> r;
// 初始化动态规划数组
f[0][0] = 1; // 将0个元素分成0个集合的方案数为1(空划分)
// 动态规划计算第二类斯特林数S(n, r)
for (int i = 1; i <= n; i++) // 遍历元素个数i从1到n
{
for (int j = 1; j <= r; j++) // 遍历集合个数j从1到r
{
// 递推公式:S(i, j) = S(i-1, j-1) + j * S(i-1, j)
// 含义:
// 1. S(i-1, j-1):第i个元素单独成一个新集合
// 2. j * S(i-1, j):第i个元素放入已有的j个集合中的任意一个,有j种选择
f[i][j] = f[i - 1][j - 1] + j * f[i - 1][j];
}
}
// 获取第二类斯特林数S(n, r)
ans = f[n][r];
// 乘以r!,将无序集合转换为有序盒子
// 因为S(n, r)计算的是无序集合的划分
// 乘以r!后得到将n个不同元素放入r个不同盒子的方案数
for (int i = 1; i <= r; i++)
{
ans *= i; // 计算r的阶乘
}
// 输出最终结果
printf("%d", ans);
return 0;
}
【运行结果】
3 2
6
浙公网安备 33010602011771号