排列组合问题
排列组合是组合数学里的两大经典问题,下面我们先来看一下它的定义:
排列:从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。
组合:一般地,从n个不同的元素中,任取m(m≤n)个元素为一组,(即不考虑顺序),叫作从n个不同元素中取出m个元素的一个组合。
一、全排列问题
首先我们可以先简化一下这个问题,就令 n = m n=m n=m,我们可以讨论一下做法。
这个问题很容易看出,我们可以简单粗暴的用dfs暴力求解,当需要排列的数字为n时,时间复杂度明显是 O ( 2 n ) O(2^{n}) O(2n),效率有点低吧,不过应该不能优化了。其中b数组用于判重。
代码如下(只有P的老师别打我):
procedure try(x:longint);
var
i:longint;
begin
if x>m then
begin
print;
exit;
end;
for i:=1 to n do
if not b[i] then
begin
a[x]:=i;
b[i]:=not b[i];
try(x+1);
b[i]:=not b[i];
end;
end;
在这里安利一波stl库的做法(STL大法好),我们可以用下面这个代码来求解n个元素的全排列问题:
cin >> a;
sort(a,a+n);
int ans=0;
do{
puts(a);
ans++;
}while(next_permutation(a,a+n));
cout << ans;
那么这个东西捏,也就是next_permutation
这个函数(划重点),它存在于c++的algorithm库中,用途是求a数组的下一个字典序排列,还有个bool类型的返回值,可以直接用于循环的判断。
那么我们现在来看一下全排列问题当 n ≤ m n\leq m n≤m时,我们又应该怎么办呢?
在我们的dfs程序里只要将输出的判断参数n改成m就行了好白痴啊233。时间复杂度$O(n^{m}) $.
二、全组合问题
明显我们可以看出,这个问题相类似与全排列问题,不过需要判断重复而已。那么其实也很容易想到,我们只需要从前往后搜,不回头就一定不会出现重复的组合并且它一定是按字典序排列的。所以我们可以在我们的dfs中增加一个参数,每次记录下当前搜到的位置,传到下一层就从这个位置的后一位继续搜。
时间复杂度,emmm,好像很难算的样子,应该可以用组合公式求出,就先卖一个关子。
在luogu上还有这样子的玄学做法:将一个数组x的m项赋值为0,其它赋值为1,先从小到大排序,就可以通过上面那一个next_permutation这个函数来求每一个x数组的每一个排列。在每一个排列中,我们可以输出x数组中值为0的项。复杂度 O ( n m ⋅ m ) O(n^{m}·m) O(nm⋅m).
代码。。给一下吧:
for(int i=r+1;i<=n;++i)
x[i]=1;
do{
for