排列组合之生成排列_(:з」∠)_
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 ;
2.递归法
长的排列可以通过固定一部分化归成短的排列
既然有了递推关系,就可以通过递归实现了。
第二类:按非字典序生成
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
2.逆序列倒推
由于每一个逆序列都确定唯一原数列。逆序列较原序列好枚举。所以通过逆序列倒退原数列。
设逆序列p1,p2,...,pn
0<=p1<=n-1,0<=p2<=n-2,...0<=pn-1<=1,pn=0;
p1,p2,...,pn 可以通过模拟n位(n-i)进制的方法枚举出逆序列。
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.所以一定放的下。
第一类:按字典序生成
参考:
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]); } }
相关文章推荐
- 浅谈全排列与组合的生成
- 用select from dual生成字母的排列组合
- 递归学习_组合_生成全子集组合排列(不含空集)
- [Bzoj3193][JLOI2013]地形生成 (排列组合 + DP)
- 排列组合,子集生成问题,与nyoj 组合数
- 排列组合算法1:生成全部有序列
- dfs生成排列组合模板
- 生成排列和组合
- Leetcode 78&90. Subsets I & II 【排列与组合的生成总结】
- 组合数学 + STL --- 利用STL生成全排列
- 一个排列、组合的生成算法 [zz]
- 随机数与排列组合:生成1亿个随机的不同16位数
- 生成排列和组合
- 排列组合数生成算法
- 组合序列、排列序列的生成实现
- 生成组合和排列
- STL生成的简单的排列组合数值
- 非重复生成全子集组合排列(含重复数字时,生成不重复全子集组合排列)
- 排列组合算法1:生成全部有序列b
- 基础算法之排列组合生成算法