排列组合算法之隔板问题与错排公式
一、获取数组元素的全排列
1.1全排列描述
给定一个数组例如[1,2,3],或['a','b','c'],获取数组元素的所有不同排列方式就叫该数组的全排列。如果数组元素个数为n,那么全排列的个数为n!。
1.2实现代码
可以通过递归方式编写代码实现,也可以使用C++标准模板库STL中的next_permutation函数实现。代码如下:
cpp
#include<bits/stdc++.h>
using namespace std;
string s1, s2;
int n;
char a[10];
int main()
{
cin>>s1;
s2=s1;
n=s1.size();
sort(s1.begin(), s1.end());
for(int i=0; i<n; i++) a[i]=s1[i];
do
{
for(int i=0; i<n; i++) cout<<a[i]<<' ';
cout<<'\n';
}while(next_permutation(a, a+n));
return 0;
}
1.3代码说明
上述代码实现了对输入的字符串输出它的全排列的功能,需要注意的是next_permutation函数是根据字典序生成当前序列的下一个排列,因此要使用do while循环形式,如果直接使用while循环会漏掉当前序列本身的一种排列。
二、组合数计算
2.1组合数描述
给定n个不同元素,从中取出m个元素,不计较顺序,求m个元素一共可以有多少种组合,这就是组合数问题,组合的数量记为: C n m C_{n}^{m} Cnm,也可以写为C(n,m),规定 C n 0 C_{n}^{0} Cn0=1, C n m C_{n}^{m} Cnm= C n n − m C_{n}^{n-m} Cnn−m。
C n m C_{n}^{m} Cnm的直接计算公式为 n × ( n − 1 ) × . . . ( n − m + 1 ) ! m ! \frac{n×(n-1)×...(n-m+1)!}{m!} m!n×(n−1)×...(n−m+1)!
递归计算公式为: C n m C_{n}^{m} Cnm= C n − 1 m C_{n-1}^{m} Cn−1m+ C n − 1 m − 1 C_{n-1}^{m-1} Cn−1m−1
将m取值0到n的组合数累加的结果等于2的n次方,公式为:C(n,0)+C(n,1)+C(n,2)+...+C(n,n)=2^n
2.2组合数递归求解代码
求解代码如下:
cpp
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n,m,t;
LL ans[16][16];
LL calc(int x, int y)
{
if(y==0 || x==y) return ans[x][y]=1;
ans[x][y]=calc(x-1, y)+calc(x-1, y-1);
return ans[x][y];
}
int main()
{
cin>>t;
for(int i=0; i<=15; i++)
{
for(int j=0; j<=i/2; j++)
{
if(ans[i][j]==0) calc(i, j);
ans[i][i-j]=ans[i][j];
}
}
while(t--)
{
cin>>n>>m;
cout<<ans[n][m]<<'\n';
}
return 0;
}
三、隔板问题
3.1隔板问题描述
将n个相同的球分为k个有序的组,问有多少种分配方案。
例如第一组4个球,第二组3个球,和第一组3个球,第二组4个球,这是两种不同的组合。
3.2求解思路
根据每组球的数量是否可以为0,分为两种隔板问题。
3.2.1每组球的数量至少为1
这种解法较为简单,n个球共有n-1个空格,从这n-1个空格中选取k-1个插入隔板,则实现了分为k个组,那么分配方案总数为 C n − 1 k − 1 C_{n-1}^{k-1} Cn−1k−1
3.2.2每组球的数量可以为0
此时可以从每个组借一个球一共借出k个球加入n个球中,那么球的总数变为n+k,k个组每组初始状态为-1个球。
问题变为将n+k个球分为k组,每组球的数量至少为1,则分配方案总数为 C n + k − 1 k − 1 C_{n+k -1}^{k-1} Cn+k−1k−1
四、错排公式
4.1错排描述
错位排列的定义是没有任何元素出现在其有序位置的排列。例如对于一个数组a,规定有序位置为a[1]=1,a[2]=2,a[3]=3,那么[2,3,1]就是一个错位排列,而[2,1,3]不是一个错位排列。
4.2错排公式
设数组大小为n,错排数为D(n),则有递推公式为D(n)=(n-1)*[D(n-1)+D(n-2)],n≥3,D(1)=0,D(2)=1。
公式推导:
- 对于第一个位置,除了对应的正确元素之外其他元素都可以摆放,因此有n-1种;
- 此时考虑第一个位置摆放的元素,它对应的正确位置可以摆放第一个位置对应的正确元素,那么剩下n-2个位置要错排n-2个元素,错排数量为D(n-2);
- 第一个位置摆放的元素,如果它对应的正确位置不摆放第一个位置对应的正确元素,那么此时相当于n-1个位置要错排n-1个元素(此处不易理解,读者可以举个具体例子帮助思考),错排数量为D(n-1);
- 因此得出D(n)=(n-1)*[D(n-1)+D(n-2)]