我们假设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(" "); }while(next_permutation(p, p+n)); return 0; }
用以上两种方法进行全排列的枚举可以避免本文刚开始案例之中dfs枚举全排列中不允许重复元素的问题,是通用的方法。