列舉排列的兩種方法:遞迴列舉 和 next_permutation()函式
阿新 • • 發佈:2018-12-29
照著《入門經典》理解整理了一下。
① 以字典序生成1~n的排列 (遞迴列舉)
運用一層層的遞迴,形成一個解答樹。
#include<cstdio>
using namespace std;
void print_permutation_1(int n,int *ar,int cur)
{ //ar儲存排列,cur表示當前ar中排列到第幾個
if(cur==n) //當cur==n,即已排列到末尾,則列印該排列
{
for(int i=0; i<n; i++)
printf("%d ",ar[i]);
printf("\n" );
}
else
{
for(int i=1; i<=n; i++) //從1~n選擇元素放入當前的ar[cur]
{
bool ok=true;
for(int j=0; j<cur; j++ ) //確保選擇的i還不在排列中
{
if(ar[j]==i)
ok=false;
}
if(ok)
{
ar[cur]=i;
print_permutation_1(n,ar,cur+1); //遞迴,cur+1
}
}
}
}
② 以字典序生成可重集的排列 (遞迴列舉)
給定一個數組P,按字典序輸出所有排列,陣列P中會有重複元素。
要先讓陣列P從小到大排好序,答案不能有重複的排列,也不能缺少元素。
#include<cstdio>
using namespace std;
void print_permutation_2(int n,int *P, int *ar,int cur)
{
if(cur==n)
{
for(int i=0; i<n; i++)
printf("%d ",ar[i]);
printf("\n");
}
else
{
for(int i=0; i<n; i++) //從P[0]到P[n-1]選擇元素放入當前ar[cur]
{
//因為之後要通過比較P和ar中該元素的數量來判斷是否選擇該元素,
//所以用以下的if來略過相同元素,以此防止出現相同排列。
if(i==0||(i!=0&&P[i]!=P[i-1]))
{
int c1=0,c2=0; //c1記錄ar中該元素的數量
for(int j=0; j<cur; j++ ) //c2記錄P中該元素的數量
{
if(ar[j]==P[i])
c1++;
}
for(int j=0; j<n; j++)
{
if(P[j]==P[i])
c2++;
}
if(c1<c2)
{
ar[cur]=P[i];
print_permutation_2(n,P,ar,cur+1); //遞迴,cur+1
}
}
}
}
}
③使用C++ STL中的全排列函式next_permutation
對於next_permutation函式,其函式原型為:
#include <algorithm> bool next_permutation(iterator start,iterator end)
若當前序列不存在下一個排列時,函式返回false,否則返回true
給next_permutation()函式提供一個原排列,就可以得到該排列的下一個排列(字典序)並返回true,到了該排列字典序的最後一個排列則返回false。
next_permutation()適用於可重集
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int n,P[10];
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",P+i);
sort(P,P+n); //生成最小的排列
do{
for(int i=0;i<n;i++)
printf("%d ",P[i]);
printf("\n");
}while(next_permutation(P,P+n)); //從 首地址 到 末地址+1
return 0;
}