目录
前言
排列组合问题实在暴力枚举的时候经常遇到的问题,是搜索技术的基础之一,是我们必须要掌握的算法。
一、全排列
递归求全排列:
#include<bits/stdc++.h>
using namespace std;
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int num = 0;
void solve(int begin, int end){
if(begin == end) num++;
else{
for(int i = begin ; i <= end; i++){
swap(data[begin], data[i]);
solve(begin+1, end);
swap(data[begin], data[i]);
}
}
}
int main(){
solve(0, 9);
cout << num << endl;
return 0;
}
STL求全排列:
1.说明:使用STL库中的next_permutation(),他按字典序输出下一个排序,一般情况下使用前需要sort()来得到最小排序。
2.代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int data[4] = {1, 2, 3, 4};
int num = 0;
do{
for(int i = 0; i < 4; i++)
cout << data[i] << " ";
cout << endl;
num++;
}while(next_permutation(data, data + 4));
cout << num << endl;
return 0;
}
二、组合问题
有些排列问题中不需要输出全排列,而是输出组合,即子集(子集内部的元素是没有顺序的)。
方法一:通过二进制数与集合子集对应的关系来计算打印组合结果。
//打印n个数中任意k个数的组合
#include<bits/stdc++.h>
using namespace std;
void print_set(int n, int k){
for(int i = 0; i < (1 << n); i++){
//i:0~2^n,每个i的二进制数对应一个子集,一次打印一个子集,最后得到所有子集
int num = 0, kk = i;
while(kk){ //计算二进制数中1的个数
kk = kk & (kk-1); //消除二进制数的最后一个1
num++;
}
if(num == k){ //二进制数中的1由k的,符合条件
for(int j = 0; j < n; j++) //打印一个子集,即打印i的二进制数中所有的1
if(i & (1 << j)) //从i的最低位开始逐个检查每一位,如果是1,打印
cout << j << " ";
cout << endl;
}
}
}
int main(){
int n, k;
cin >> n >> k;
print_set(n, k);
return 0;
}
方法二:采用排列组合公式计算
#include<iostream>
#include<stdio.h>
using namespace std;
//利用公式c(n, m) = n*(n-1)*(n-2)*...*(n-m+1) / m!进行计算
long long c(int n, int m){
long long c;
if(n < m)
c = 0;
else if(n == m || m == 0)
c = 1;
else{
c = 1;
n = n - m + 1;
for(int i = 1; i <= m; i++){
c *= n++;
c /= i;
}
}
return c;
}
int main(){
int t, n, m;
cin >> t;
while(t--){
cin >> n >> m;
int ans = c(n, m);
cout << ans << endl;
}
return 0;
}