洛谷P1287 盒子与球(dp/组合数学(第二类斯特林数变形))

题目

现有 r 个互不相同的盒子和 n个互不相同的球,要将这 n 个球放入 r 个盒子中,且不允许有空盒子。

请求出有多少种不同的放法,两种放法不同当且仅当存在一个球使得该球在两种放法中放入了不同的盒子。

数据范围:0<=r<=n<=10

思路来源

https://www.luogu.com.cn/problemnew/solution/P1287

https://wtfhcn.github.io/2020/02/28/%E7%AC%AC%E4%B8%80%E5%92%8C%E7%AC%AC%E4%BA%8C%E7%B1%BB%E6%96%AF%E7%89%B9%E6%9E%97%E6%95%B0/

题解

这是普及的题你敢信orz

组合数学真的菜啊orz,还好洛谷的题解比较清晰

题解1:dp[i][j]代表i个不同球j个相同盒子的方案数,第二类斯特林数,求一下n个不同球r个相同盒子的方案数,然后乘一下r个盒子的顺序即可

#include
#include
#include
using namespace std;
typedef long long ll;
const int N=10;
int n,r,fac[N],dp[N][N];
int main(){
	scanf("%d%d",&n,&r);
	dp[0][0]=fac[0]=1;
	for(int i=1;i<=n;++i){
		fac[i]=fac[i-1]*i;
		for(int j=1;j<=min(i,r);++j){
			dp[i][j]=dp[i-1][j-1]+j*(dp[i-1][j]);
		}
	}
	printf("%d\n",dp[n][r]*fac[r]);
	return 0;
}

题解2:dp[i][j]代表i个不同球j个不同盒子的方案数(直接做),

最后一球放入单独一盒子(选一个位置),共j*dp[i-1][j-1]种,

最后一球共用盒子(选一个盒子),则有j*dp[i-1][j]种

#include
#include
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
const int N=15;
int n,r,f[N][N];//f[i][j]:i个球j个盒子的方案数 
int main(){
	f[0][0]=1;
	scanf("%d%d",&n,&r);
	rep(i,1,n){
		rep(j,1,min(i,r)){
			f[i][j]=j*(f[i-1][j-1]+f[i-1][j]);
		}
	}
	printf("%d\n",f[n][r]);
    return 0;
}

题解3:f[i]表示i个盒子且都放了球的方案数,

用当前所有方案(n个球放在i个盒子里随便放),

减去选1个盒子只在1个盒子里放球(C_{i}^{1}*f(1)),...,减去选i-1个盒子只在i-1个盒子里放球(C_{i}^{i-1}*f(i-1))……

最后,就只剩在i个盒子里放球了的方案了

 

 

你可能感兴趣的:(#,组合数学(容斥原理))