我們假設A數組是方案數組,P數組是模板數組。
對於每一種方案,從第一個位置開始放元素,一個一個放。
我們原有的打印全排列的方法是不允許A數組中出現重復元素的,如下代碼所示:
void dfs(int dp) { if(dp>n) { for(int i=1;i<=n;i++) cout<<a[i]<<" "; ans++; cout<<endl; return; } for(int i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; a[dp]=i; dfs(dp+1); a[dp]=0; vis[i]=0; } } }
解決方案如下
我們在放每一個元素的時候,對於當前將要放的元素P[i],需要考慮已經在A數組中放置的元素里面(A[0]~A[cur-1]),P[i]出現的次數c1,以及模板數組P中P[i]出現的次數c2
如果滿足c1<c2就可以繼續遞歸調用
for(int k=0;k<cur;k++) if(P[i]==A[k]) c1++; for(int k=0;k<n;k++) if(P[i]==P[k]) c2++;
以上的解決方案可以避免遺漏,但是會出現重復
我們枚舉的下標i應該不重復不遺漏地取模板數組中的每一個P[i],為了解決這一問題,我們事先將P數組從小到大排序,然后進行檢查即可(相同的元素因排序會挨在一起)
if(!i||P[i]!=P[i-1])
之后便可以得到正確的結果。完整的代碼如下,程序讀入n和n個數,打印全排列。
#include<iostream> #include<algorithm> using namespace std; const int maxn=15; int P[maxn],A[maxn]; void dfs(int cur,int n) { if(cur==n) { for(int i=0;i<n;i++) cout<<A[i]<<" "; cout<<endl; return; } for(int i=0;i<n;i++) { if(!i||P[i]!=P[i-1]) { int c1=0,c2=0; for(int k=0;k<cur;k++) if(P[i]==A[k]) c1++; for(int k=0;k<n;k++) if(P[i]==P[k]) c2++; if(c1<c2) { A[cur]=P[i]; dfs(cur+1,n); } } } } int main() { int n=0; cin>>n; for(int i=0;i<n;i++) scanf("%d",P+i); sort(P,P+n); dfs(0,n); return 0; }
當然,我們完全可以把這個任務交給STL去完成,STL中的next_permutation函數會自動處理元素重復這一細節,用STL實現的代碼如下:
#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)); return 0; }
用以上兩種方法進行全排列的枚舉可以避免本文剛開始案例之中dfs枚舉全排列中不允許重復元素的問題,是通用的方法。
