排列組合(兩種方法)

排列組合是組合數學的基礎,從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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章