排列组合(两种方法)

来源:互联网 发布:网络销售是做什么的 编辑:程序博客网 时间:2024/05/17 17:58

排列组合是组合数学的基础,从n个不同元素中任取m个,约定1<m≤n,按任意一种次序排成一列,称为排列,其排列种数记为(Arrange)A(n,m)。从n个不同元素中任取m个(约定1<m<n)成一组,称为一个组合,其组合种数记为C(n,m)。计算A(n,m)与C(n,m)只要简单进行乘运算即可,要具体展现出排列的每一列与组合的每一组,决非轻而易举。
注意:
1、 排列A有序 A(n,m) = n * (n-1) * …*(n-m+1)
2、 组合C无序 C(n,m) = A(n,m) / A(m,m)
3、 注意到组合与组成元素的顺序无关,约定组合中的组成元素按递增排序。因而,把以上程序中的约束条件作简单修改: a[i]==a[j] 修改为 a[i]>=a[j]
有两种方法:
1、DFS方法适用于不重复的情况下,分治法可适用于有元素相同的情况下
2:分治法元素相同时的情况为:
//1、arr[i]!=arr[k],因为这样交换后剩下的元素是一样的,两种全排列是重复的
//2、arr[k+1]—>arr[i-1],也就是k和i之间的元素不能和arr[i]相等,因为k和i之间的元素都是和k交换过的,
//如果arr[i]和arr[k+1]—>arr[i-1]之间的元素有相等的,那么这两种排列也是重复的
//下面代码中||运算前的是第一种情况,或运算之后的是第二种情况

排列:DFS方法一:回溯法(全排列的数组中不可以有重复的元素)/*1-n中m个数的全排列*/#include <iostream>#include <vector>using namespace std;int count = 0;vector<int> v;bool Single(int num){    for (int i=0; i<v.size(); ++i)    {        if (v[i] == num)            return false;     }    return true;}//全排列void perm(int n, int m){    if (m == 0)    {        ++count;        return;    }    for (int i=1; i<=n; ++i)     {        if (Single(i))        {            v.push_back(i);            perm(n, m-1);            v.pop_back();         }    }}int main(){    int n, m;    cin >> n >> m;    perm(n, m);     cout << count << endl;    return 0;}
回溯法:组合(组合的数组中不可以有重复的元素)/*1-n中m个数的组合*/#include <iostream>#include <vector>using namespace std;int count = 0;vector<int> v;bool Single(int num){    for (int i=0; i<v.size(); ++i)    {        if (v[i] == num)            return false;     }    return true;}//组合void perm(int n, int m){    if (m == 0)    {        ++count;        return;    }    for (int i=1; i<=n; ++i)     {        if (Single(i))        {            v.push_back(i);            perm(n, m-1);            v.pop_back();         }    }}int main(){    int n, m;    cin >> n >> m;    perm(n, m);     cout << count << endl;    return 0;}
全排列方法二:分治法(数组中可以有重复元素,下一个程序就是怎样消除重复)#include <iostream>using namespace std;int count = 0;void swap(int* arr, int i, int j){    arr[i] = arr[i] ^ arr[j];    arr[j] = arr[i] ^ arr[j];     arr[i] = arr[i] ^ arr[j]; }void perm(int* arr, int len, int m, int n){    //生成了一种全排列     if (m == n)    {        ++count;        return;     }    for (int i=m; i<=n; ++i)    {        swap(arr, m, i);        perm(arr, len, m+1, n);         swap(arr, m, i);     }}int main(){    int arr[9];    for (int i=0; i<9; ++i)    {        arr[i] = i+1;     }    perm(arr, 9, 0, 8);     cout << count << endl;    return 0;}
分治法:全排列(数组中可以有重复的元素ADCD)#include <iostream>#include <string>using namespace std;string str;string* pwd;int n;int count = 0;void swap(int index, int i, int j){    char tmp = pwd[index][i];    pwd[index][i] = pwd[index][j];    pwd[index][j] = tmp; }//判断是否重复出现//一般的全排列算法只能排列数组中没有重复的数字或字母//但是如果出现重复的情况下比如aaaabbbb进行全排列,好多排列虽然看似不重复,但是结果是重复的//这种方法进行全排列时(分治法),从1-n个元素中选n次,每次选1个之前不同的数,然后对剩下的数进行全排列//也就是perm(R)=(r1)perm(R1),(r2)perm(R2),(r3)pwem(R3)....(rn)perm(Rn)//每次i和k交换的时候,重点是://1、arr[i]!=arr[k],因为这样交换后剩下的元素是一样的,两种全排列是重复的//2、arr[k+1]--->arr[i-1],也就是k和i之间的元素不能和arr[i]相等,因为k和i之间的元素都是和k交换过的,//如果arr[i]和arr[k+1]--->arr[i-1]之间的元素有相等的,那么这两种排列也是重复的//下面代码中||运算前的是第一种情况,或运算之后的是第二种情况 bool Appear(int index, char target, int begin, int end){    for (int i=begin; i<=end; ++i)    {        if (target == pwd[index][i])            return true;     }    return false;}void perm(int index, int k, int m){    //得到一种全排列     if (k == m)     {        //判断在不在str中        if (str.find(pwd[index]) != -1)         {            ++count;        }        return;    }    //全排列    for (int i=k; i<=m; ++i)    {        //判断字母是否重复        if ((i!=k && pwd[index][i]==pwd[index][k]) || Appear(index, pwd[index][i], k+1, i-1))            continue;        swap(index, k, i);        perm(index, k+1, m);        swap(index, k, i);     }}int main(){    cin >> str;    cin >> n;    pwd = new string[n];     for (int i=0; i<n; ++i)        cin >> pwd[i];    for (int i=0; i<n; ++i)    {        perm(i, 0, 7);     }    cout << count << endl;    delete[] pwd;     return 0;}
0 0
原创粉丝点击