n进位法
这个算法是将初始排列012……(n-1)作为一个n进制数,然后对它累加1,从而生成全体排列。这个算法比较简单,但是在累加过程中,会出现重复元素,所以需要将有重复元素的排列删除。这是这个算法的缺点。
从一个排列生成一个新的排列的算法如下:
第一步:在排列的第n-1 位累加1。
第二步:如果其和大于n-1,就进位,发生连续进位就连续进位直至不再发生进位为止。无进位继续。
第三步:检查排列中有无重复元素,如果有则直接返回第一步;如果无则输出一个排列。
要产生n个元素的全排列,可以从原始排列01……(n-1)开始,对第n-1位累加1,产生一个新的排列后就输出,然后返回重复以上步骤,直到排列的第一个元素超过n-1时停止。
以3个数0、1、2的排列为例:原始排列是1 2 3,从它开始,最后一个元素是3,加上1则有3+1=4,4>3,进位,前面一个元素是2,进位后2+1=3,所以新排列是1 3 1,这个排列有重复元素,将它抛弃,继续对最后一个元素加1,……。按照这个访法,顺序产生的排列是:1 2 3,1 3 2,1 3 3,2 1 1,2 1 2,2 1 3,2 2 1,2 2 2,2 2 3,2 3 1,2 3 2,2 3 3,3 1 1,3 1 2,3 1 3,3 2 1,3 2 2,3 2 3,3 3 1,3 3 2,3 3 3,4 1 1,这时第一个元素大于3,程序结束。把其中有重复元素的排列去掉,就得到了三个元素的排列。有下划线的排列中存在重复元素,丢弃,余下的就是全部排列。
需要注意,这里并非严格意义的n进制数,因为每一项进位时是在该项大于n才发生。这样做的目的是为了保证按照给定元素实现排列。
//n进位算法
//输入:排列元素个数n
//输出:n个元素的排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *p,int n);
void output(int *p,int n);
int repeat(int *p,int n);
int total;
int main()
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n)
{
p=new int[n];
for(inti0;i<n;i++) //数组初始化
p[i]=i+1;
total=0;
perm(p,n);
}
return 0;
}
void perm(int *p,int n)
{
while(1)
{
if(!repeat(p,n)) //排列中无重复元素
output(p,n); //输出一个排列
int i=n-1; //在排列末尾累加1
p[i]++;
while(p[i]>n && i>0) //有进位向前进位
{
p[i--]-=n;
p[i]++;
}
if(p[0]>n) //排列第一位大于n
return; //结束
}
}
void output(int *p,int n)
{
cout<<setw(10)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
int repeat(int *p,int n)
{
for(int i=0;i<n-1;i++)
for(int j=i+1;j<n;j++)
if(p[i]==p[j])
return 1;
return 0;
}