此文写于2017-11-25
有重复数字的全排列
全排列就不说了,一个next_permutation就解决了。
但是有重复的数字还能用基本的全排列吗?
答案显然是不能的,我们需要一些语句来帮助我们避免重复。
方法一
让我们结合代码来分析
//有重复的全排列
#include<cstdio>
#include<algorithm>
using namespace std;
const int MN=105;
int a[MN],n,x,ans[MN],l;
bool vis[MN];
void print()
{
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
}
void dfs(int k)
{
if(k>n) {print();return;}
int last=-1;
for(int i=1;i<=n;i++)
if(vis[i]==false&&a[i]!=last)
vis[i]=true,ans[k]=a[i],last=a[i],dfs(k+1),vis[i]=false;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
dfs(1);
return 0;
}
我们主要来说说这个dfs函数
如果k>n,那么就说明到了边界,直接输出;
关键是中间的判断重复与查找排列这一部分了
这段代码采用的是判断是否与上一个数字重复–可能不太好理解,我举个例吧:
例如输入的是:1 2 2
那么它的全排列就是:
①1 2 2
②2 1 2
③2 2 1
执行过程是这样的
首先进去就是1 2 2
然后返回变成1 2
发现不能是1 2 2
然后返回成1
发现不能是1 2
然后返回,更新成2
然后更新成2 1 2
返回变成2 1
返回变成2
更新成2 2 1
完毕
方法二
我们发现,我们可以直接利用数字出现的次数来做,方法如下,看代码就会(计数法)(约定一下数字大小不超过30)
#include<cstdio>
#include<algorithm>
using namespace std;
int b[105];
int ans[105],n,a[105],cnt;
void dfs(int k)
{
if(k>n)
{
for(int i=1;i<n;i++) printf("%d ",ans[i]);
printf("%d\n",ans[n]);
return;
}
for(int i=1;i<=30;i++)
if(b[i])
{
ans[k]=i;
b[i]--;
dfs(k+1);
b[i]++;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[a[i]]++;
}
sort(a+1,a+n+1);
dfs(1);
return 0;
}