盒与球问题

典题合集

编号 球是否相同 盒子是否相同 是否允许空盒 思路 扩展
1 相同 不同 组合数
2 相同 不同 组合数
3 相同 相同 dp
4 相同 相同 dp 在3思路上扩展
5 不同 相同 dp
6 不同 相同 dp 在5思路上扩展
7 不同 不同 dp 在5思路上扩展
8 不同 不同 组合数

规定n:球数,m:盒子数

1.球相同,盒子不同,无空盒

挡板法,相当于将n个球分成m组,相当于在n1位置中插入m1块板子。
[公式]

\(C_{n-1}^{m-1}\)

2.球相同,盒子不同,有空盒

我们可以假设每个盒子里都已经放了一个球,这个时候实际上就有 N+M个球了,我们再次使用隔板法。对于每一种方案,我们就可以想象成每个空隙右边的第一个球是提前放好在盒子里的(即盒子内只有一个球),如果将这个球省略,正好就是盒子允许为空的一种方案。

[公式]

\(C_{N+M-1}^{M-1}\)

3.球相同,盒子相同,有空盒

假设f[n][m]n个球放到m个盒子里的方案数。

如果n<m,此时m个盒子必然装不满,f[n][m]=f[n][n]。

如果n>=m,此时可以选择将盒子放满或者盒子不放满。

(1)如果没放满,那就减掉一个盒子,此时为f[i][j1](即由当前第j个盒子为空盒子)。

(2)如果放满了,那就在每个盒子上里放一个球。现在就是f[i-j][j]。(即i-j个球放j个盒子的情况)。

[公式]

f[i][j]=f[i][j1]+f[ij][j]

如果没有球或者只有一个盒子,此时方案数为1,即f[0][j]=f[i][1]=1

[时间复杂度]

O(nm) n:球数,m:盒子数

const int N=1010;
ll f[N][N];//f[n][m]为n个球放到m个盒子里的方案数
void init(int n,int m){//n:球数,m:盒子数
	for(int i=0;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(j==1||i==0) f[i][j]=1;
			else if(i<j) f[i][j]=f[i][i];
			else if(i>=j) f[i][j]=f[i-j][j]+f[i][j-1];
		}
	}
}
ll get_schemes(int n,int m){//n:球数,m:盒子数,允许n=0,但m!=0
	init(n,m);
	return f[n][m];
}

4.球相同,盒子相同,无空盒

这种情况可以由(3.球相同,盒子相同,有空盒)情况推到出来

当球的数量小于盒子的数量:有0种可能

当球的数量等于盒子的数量:有1种可能

当球的数量大于盒子的数量:先把每个盒子放上一个球,然后问题就是第一种情况:f[n-m][m]。

const int N=1010;
ll f[N][N];//f[n][m]为n个球放到m个盒子里的方案数
void init(int n,int m){//n:球数,m:盒子数
	for(int i=0;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(j==1||i==0) f[i][j]=1;
			else if(i<j) f[i][j]=f[i][j];
			else if(i>=j) f[i][j]=f[i-1][j]+f[i][j-1];
		}
	}
}
ll get_schemes(int n,int m){
	init(n,m);
	if(n<m) return 0;
	else if(n==m) return 1;
	else return f[n-m][m]; 
}

5.球不同,盒子相同,无空盒

用f[i][j]记录i个球放到j个盒子的情况,首先可以对f[i][j]进行初始化的操作。

(1)就是i个球放到i个盒子的可能性为1

(2)f[i][j]可以由两种情况推导得到

  • 第i个球放到新开一个盒子,假设j就是那个新开的盒子,那么f[i][j]就是i-1个球放到j-1个盒子的情况
  • 第i个球放到之前的盒子,那么结果可以由i-1个球放到j个盒子的情况得到,并且第i个球有j种选择
  • 公式:f[i][j]=f[i-1][j-1]+j*f[i-1][j];

[第二类斯特林数]

定义:将n个物品分成k个非空集合的方案数,记作\(\left.\left\{\begin{matrix}n\\k\end{matrix}\right.\right\}\),称为斯特林子集数.

公式:\(\left.\left\{\begin{array}{c}n\\k\end{array}\right.\right\}=k\left\{\begin{array}{c}n-1\\k\end{array}\right\}+\left\{\begin{array}{c}n-1\\k-1\end{array}\right\}\)

意义:放入最后一个物品时,若新成立一个集合,方案数为\(\left.\left\{\begin{array}{l}n-1\\k-1\end{array}\right.\right\}\),若加入原来的集合,方案数为\(\left.k\left\{\begin{gathered}n-1\\k\end{gathered}\right.\right\}\)

const int N=1010;
ll f[N][N];//f[n][m]为n个球放到m个盒子里的方案数
void init(int n,int m){//n:球数,m:盒子数
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			f[i][j]=f[i-1][j-1]+j*f[i-1][j];
		}
	}
}
ll get_schemes(int n,int m){
	init(n,m);
	return f[n][m]; 
}

6.球不同,盒子相同,有空盒

对(5.球不同,盒子相同,无空盒)扩展,只需要枚举非空盒子的对应的方案数量即可。

const int N=1010;
ll f[N][N];//f[n][m]为n个球放到m个盒子里的方案数
void init(int n,int m){//n:球数,m:盒子数
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			f[i][j]=f[i-1][j-1]+j*f[i-1][j];
		}
	}
}
ll get_schemes(int n,int m){
	init(n,m);
	ll res=0;
	for(int i=1;i<=m;i++){
		res+=f[n][i];
	}
	return res; 
}

7.球不同,盒子不同,无空盒

对(5.球不同,盒子相同,无空盒)扩展,我在第5种的情况下,考虑盒子不同的问题。

盒子不同的情况,球它的一个排列。

const int N=20;
ll f[N][N];//f[n][m]为n个球放到m个盒子里的方案数
ll b[N];
void init(int n,int m){//n:球数,m:盒子数
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=i;j++){
			f[i][j]=f[i-1][j-1]+j*f[i-1][j];
		}
	}
	b[0]=1;
	for(int i=1;i<=m;i++){//求m!
		b[i]=b[i-1]*i;
	}
}
ll get_schemes(int n,int m){
	init(n,m);
	return b[m]*f[n][m];//可能会爆long long 
}

8.球不同,盒子不同,有空盒

每个球都有对应盒子的选择,即盒子^(球)。

ll qmi(ll a,ll b){
	ll res=0;
	while(b){
		if(b&1) res=res*a;
		a=a*a;
		b>>=1;
	}
	return res;
}
ll get_schemes(int n,int m){
	return qmi(m,n);//可能会爆long long 
}
posted @ 2023-12-06 20:01  White_Sheep  阅读(37)  评论(0编辑  收藏  举报