您的位置:首页 > 其它

排列组合之生成排列_(:з」∠)_

2014-08-19 11:42 323 查看
1.生成全排列





第一类:按字典序生成

参考:
http://www.cnblogs.com/pmars/archive/2013/12/04/3458289.html

1.字典序法


通过一个已知排列,生成下一个,实质是改变尽量短的后缀,使两个排列的差尽量小。

步骤:
1.从右至左选出第一个i,p[i]<p[i+1];
2.从右至i,选出第一个j,p[i]<p[j];
3.交换p[i],p[j];
4.倒置i+1~n ;

#include<stdio.h>
#include<algorithm>
using namespace std;
int c[1010][1010];
int p[1010];
int n,cnt;
void Deal()
{
cnt=0;
for(int j=1;j<=n;j++)p[j]=j,printf("%d",p[j]),c[cnt][j]=p[j];
int i=n-1;cnt++;
while(1)
{
while(i>0&&p[i+1]<p[i])i--;
if(i==0)break;
int j=n;
for(;j>i;j--)
{
if(p[j]>p[i])break;
}
swap(p[i],p[j]);
for(i=i+1,j=n;i<=j;i++,j--)
swap(p[i],p[j]);
for(j=1;j<=n;j++)
{
printf("%d",p[j]);
c[cnt][j]=p[j];
}
printf("\n");
cnt++;
i=n-1;
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Deal();
}
return 0;
}


2.递归法

长的排列可以通过固定一部分化归成短的排列
既然有了递推关系,就可以通过递归实现了。
#include<stdio.h>
#include<algorithm>
using namespace std;
int c[1010][1010];
int p[1010];
int cnt=0;
int n;
void Deal(int k)
{
if(k==n)
{
for(int i=1;i<=n;i++)
{
printf("%d",p[i]);
c[cnt][i]=p[i];
}
cnt++;
printf("\n");
return;
}
sort(p+k,p+n+1);
for(int i=k;i<=n;i++)
{
swap(p[i],p[k]);
Deal(k+1);
swap(p[i],p[k]);
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
cnt=0;
for(int i=1;i<=n;i++)
p[i]=i;
Deal(1);
}
return 0;
}


第二类:按非字典序生成

1.邻位交换法

n个元素的排列与n-1个元素的排列有关,因为如果我们已知n-1个元素的排列情况,可以通过不断插入第n
个元素生成n个元素的排列。即(n-1)!*n=n!
比如:1 2 3 1 3 2 3 1 2

2 1 3 2 3 1 3 2 1
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,cnt;
int c[1010][1010];
int p[1010];
void Copy()
{
printf("cnt:%d ",cnt);
for(int i=1;i<=n;i++)
c[cnt][i]=p[i],printf("%d",p[i]);
printf("\n");cnt++;
}
void Deal()
{
cnt=0;
int num=2;
for(int i=3;i<n;i++)
{
num*=i;
}
for(int i=1;i<=n;i++)p[i]=i;
num/=2;
while(num--)//S型插入
{
for(int i=n;i>1;i--)
{
swap(p[i],p[i-1]);
Copy();
}
swap(p
,p[n-1]);Copy();//生成新的n-1排列
for(int i=1;i<n;i++)
{
swap(p[i],p[i+1]);
Copy();
}
swap(p[1],p[2]);Copy();//生成新的n-1排列
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Deal();
}
return 0;
}


2.逆序列倒推

由于每一个逆序列都确定唯一原数列。逆序列较原序列好枚举。所以通过逆序列倒退原数列。
设逆序列p1,p2,...,pn
0<=p1<=n-1,0<=p2<=n-2,...0<=pn-1<=1,pn=0;
p1,p2,...,pn 可以通过模拟n位(n-i)进制的方法枚举出逆序列。
#include<stdio.h>
int n,cnt=0;
int c[1010][1010];
int p[1010];
int s[1010];
void cal()
{
for(int i=1;i<=n;i++)
{
int num=0;
for(int j=1;j<=n;j++)
{
if(c[cnt][j]==0)num++;
if(p[i]+1==num)
{
c[cnt][j]=i;break;
}
}
}
for(int i=1;i<=n;i++)
printf("%d",c[cnt][i]);
printf(" ");
for(int i=1;i<=n;i++)
printf("%d",p[i]);
printf("\n");
cnt++;
}
void Deal()
{
s[n-1]=1;cnt=0;
for(int i=1;i<=n;i++)
{
c[cnt][i]=i;
printf("%d",i);
}
cnt++;
printf("\n");
while(1)
{
p
=0;
for(int i=n-1;i>=1;i--)
{
p[i]+=s[i];
if(p[i]>n-i)
{
p[i-1]++;
p[i]-=n-i+1;
}
}
if(p[0]==1)break;
cal();
}

}
int main()
{
while(scanf("%d",&n)!=EOF)
{
Deal();
}
return 0;
}


2.由逆序列构建原排列

前面有些许涉及,这里再详细说下。
设逆序列为b1,b2,....,bn.所对应原排列为a1,a2,...,an
设置n个空位。
从1开始放。
由于1前面至少有b1个数,所以a1前至少有b1个空位。因为1之前没有放数,所以1放在a[b1]+1处.空位为
a_1~a_b1.
下面放2.
同样,2前面至少有b2个数,因为这b2个数全部大于2,还没有放进排列,所以2前要有把b2个空位(比2小的
已经放进排列中了。)。2放在第b2+1个空位处。
……
下面放k。
k前面至少有bk个数,k放在第bk+1个空位处(比k小的已经放进排列了)。

剩下的空位为n-k+1。bk的取值范围为0<=bk<=n-k,1<=bk+1<=n-k+1.所以一定放的下。

#include<stdio.h>
int b[10010],a[10010];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
for(int i=0;i<n;i++)
{
int cnt=0;
for(int j=0;j<n;j++)
{
if(a[j]==0)cnt++;
if(cnt==b[i]+1)
{
a[j]=i+1;break;
}
}
}
for(int i=0;i<n-1;i++)
printf("%d ",a[i]);
printf("%d\n",a[n-1]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: