一、排列
1.定义
n个元素的集合A中任意选择r个( n ≤ r n\leq r n≤r)进行排列称为A的一个r-排列/r-Permutation
n个元素的集合A的r-排列数为
p
(
n
,
r
)
=
n
(
n
−
1
)
.
.
.
1
=
n
!
/
(
n
−
r
)
!
p(n,r)=n(n-1)...1=n!/(n-r)!
p(n,r)=n(n−1)...1=n!/(n−r)!
2.理解
将n个元素选择r个元素出来(无顺序),然后进行全排列(有顺序)。
二、组合
1.定义
n个元素的集合A中任意选择r个( n ≤ r n\leq r n≤r)称为A的一个r-组合/r-Combination
n个元素的集合A的r-组合数为
C ( n , r ) = n ( n − 1 ) . . . 1 / r ! = n ! / r ! ( n − r ) ! C(n,r)=n(n-1)...1/r!=n!/r!(n-r)! C(n,r)=n(n−1)...1/r!=n!/r!(n−r)!
推论:
C ( n , r ) = C ( n , n − r ) C(n,r)=C(n,n-r) C(n,r)=C(n,n−r)
2.理解
将n个元素选择r个元素出来(无顺序)。$ C(n,r)=P(n,r)/r!$表示在排列的基础上除以r的全排列(相当于去掉顺序)
三、排列组合常见模型
1.r-可重排列
1.1.定义
n个不同元素的集合A,每个元素都可以重复选取,然后进行排列。
定理1:
n个不同元素的集合A,选取r个元素,可重排列的选择总数有**$n^r $**
1.2.理解
可重排列顾名思义,从大集合中选择的时候可以重复选取,然后再进行排列。
1.3.例题
输入的字母,输出所有的可重排序方式(每个字母可以重复)
示例
input:['a','b']
output:aa ab bb ba
代码
#include<iostream>
#include<vector>
using namespace std;
vector<char> res(3);//排列结果
//深度优先搜索方法进行可重排列
void dfs(const vector<char>& chars,int i) {
if (i == chars.size()) {
//打印输出chars
for (char c : res) {
cout << c;
}
cout << endl;
return;
}
for (char c : chars) {
res[i] = c;
dfs(chars,i + 1);
}
}
void permutation(vector<char>& chars) {
dfs(chars,0);
};
int main() {
vector<char> chars{ 'a','b','c' };//待排列的字母
permutation(chars);
return 0;
}
运行结果
2. r-可重组合
2.1.定义
n个不同元素的集合A,每个元素都可以重复选取,然后进行组合。
例:从包含1美元、2美元、5美元、10美元、20美元、 50美元及100美元的钱袋中选5张纸币,有多少种方式?假定不管纸币被选的次序,同币值的纸币不加区别,且至少每种纸币有5张。
解:7种纸币相当于7个房间,将5张纸币放入7个房间中,每个房间最多5张纸币。可以抽象成6个隔板(7个房间有6个隔板)与5张纸币进行组合。问题转换为6个隔板和5张纸币混合在一起,将其同等看待,一共有11种选择,随机选择其中5种进行组合。
C ( 5 + 7 − 1 , 5 ) = 11 ! / 6 ! 5 ! C(5+7-1,5)=11!/6!5! C(5+7−1,5)=11!/6!5!
2.2.理解
可重组合表示从不同类别中选取,每个类别可以重复选取。用隔板法来比较好理解。
3. n-限重排列
3.1.定义
n个元素进行全排列,但是要去除相同元素的顺序问题。
例 SUCCESS中有7个字符,4个不同的字符,S重数为3,C重数为2,E重数为1,U重数为1,它的7-限重排列数是多少?
C ( 7 , 3 ) C ( 4 , 2 ) C ( 2 , 1 ) C ( 1 , 1 ) = 7 ! / ( 3 ! 2 ! 1 ! 1 ! ) C(7,3)C(4,2)C(2,1) C(1,1)=7!/(3!2!1!1!) C(7,3)C(4,2)C(2,1)C(1,1)=7!/(3!2!1!1!)
3.2.理解
限重排列就是在全排列的基础上去除部分元素的顺序问题。注意和可重排列区分,可重排列是每个元素都可以重复,并且要考虑顺序。
4. r循环排列
4.1.定义
n个元素进行全排列,但是要形成一个首尾相连的环。
n个元素的集合A的r循环排列数为P(n,r)/r
4.2.理解
为什么要除以r因为形成环之后,第一位不会对排列数做贡献,参与排列每个元素都可以做第一个,所以要除以r.
四、代码生成排列和组合
1.排列
1.1可重排列(见三 1.3.例题 )
1.2全排列
代码
class Permutation
{
public:
Permutation(string base):base(base) {
}
void printPermutation(int index=0) {
if (index == base.length()) {
cout << base << endl;
}
//每个位置都轮流选以index为起点的字母(保证了每个字母只选一次)
for (int i = index; i < base.length(); i++)
{
//正在安排字符串的位置 交换为i位置的字符
if (i != index) {
swap(base[i],base[index]);
}
printPermutation(index + 1);//去安排字符串下一个位置的字母
//还原交换的字母,便于下次排列
if (i != index) {
swap(base[i], base[index]);
}
}
}
private:
string base;//待排序的字母
};
运行结果
2.组合
待续…