特殊排列的算法
2016-01-13 10:24
169 查看
全排列是集合中全部元素的排列,并且每一个排列中的元素都不会重复。实际应用中,还会遇到各种特殊的排列,例如集合中一部分元素的排列,允许元素重复的排列等。
现在来看一看产生各种特殊排列的算法。
递归形式的回溯算法如下:
//全排列(递归)
//输入:排列元素个数n
//输出:n个元素的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n)
{
p=new int
;
total=0;
backdate(p,n);
}
return 0;
}
void backdate(int *p,int n,int k) //递归生成排列
{
if(k==n) //如果排列的n个元素的值都已选定
output(p,n); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1到n中为p[k]选取一个值i
{
for(int j=0;j<k;j++) //所选的i是否与已选元素的值重复
if(p[j]==i)
break;
if(j==k) //不重复
{
p[k]=i; //选定
backdate(p,n,k+1); //继续为下一个元素p[k+1]选取值
}
}
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
生成n个元素的全排列过程中,有两个必要的条件:第一,n个排列元素的之都要选定,第二,n个排列元素要互不相同。既然如此,只要有其中一个条件发生变化,或两个条件都改变,所生成的排列就不再是全排列了。因此,可以从特殊排列的要求出发,改变排列元素的检验条件,就可以生成所需要的排列。
一.从n个不同元素中区m(m≤n)个的排列
在n个不同元素中,任选m个元素的排列可称为部分排列或选排列。显然,m≤n。如果m=n,那么就是全排列,所以,如果不做特别说明,可以认为m<n。
在n个元素的全排列中,每次所选取的元素个数必须要达到元素总数n,才输出一个排列。那么,现在只要须选定m个元素,就输出一个排列,所得的排列就是一个n个元素中选取m个元素的排列了。
以回溯法的递归程序为例,只要将代码按照代码中的黑体部分改变即可。
//n个元素取m个的排列(递归)
//输入:元素总数n,排列元素个数m
//输出:n个元素取m个的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,m,*p;
while(cin>>n>>m)
{
p=new int
;
total=0;
backdate(p,n,m,0);
}
return 0;
}
void backdate(int *p,int n,int m,int k) //递归生成排列
{
if(k==m) //如果排列中有m个元素的值被选定
output(p,m); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1 到n中为元素p[k]选取一个值
{
for(int j=0;j<k;j++) //所选的值i是否与已选元素的值重复
if(p[j]==i)
break;
if(j==k) //不重复
{
p[k]=i; //p[k]取值i
backdate(p,n,m,k+1); //继续为下一个元素选取值
}
}
}
4000
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
二.允许元素重复的全排列
一般情形,全排列中所有的元素都不相同。如果允许排列的元素重复,那就是允许可重复的排列。对于允许重复的全排列,只需要将生成全排列的算法中,将检验排列元素重复的代码去掉即可。
//元素可重复全排列(递归)
//输入:排列元素个数n
//输出:n个元素可重复的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n>>m)
{
p=new int
;
total=0;
backdate(p,n,0);
}
return 0;
}
void backdate(int *p,int n,int k) //递归生成排列
{
if(k==n) //如果排列的n个元素的值都已选定
output(p,n); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1 到n中为元素p[k]选取一个值
{
// for(int j=0;j<k;j++) //所选的值i是否与已选元素的值重复
// if(p[j]==i)
// break;
// if(j==k) //不重复
{
p[k]=i; //p[k]取值i
backdate(p,n,k+1); //继续为下一个元素选取值
}
}
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
三.指定元素可重复的全排列
如果在n个元素的全排列中,有一个元素可重复,而其余的元素不重复,生成这样的全排列。
在生成全排列的过程中,对每一个排列的元素,都要检验他是否与前面选出的元素重复,以此来保证每一个元素不会重复出现。既然如此,在要求某一个元素可以重复的情形,可以不对该元素进行重复出现的检验,它就会重复出现在排列中了。
//指定元素可重复的全排列
//输入:元素个数n,指定可重复元素x
//输出:要求的排列
#include <iostream>
#include <iomanip>
using namespace std;
void perm(int*,int,int,int);
void output(int*,int);
int total;
int main(void)
{
int n=5,x=3,*p; //x是指定可重复的元素
p=new int
;
fill_n(p,n,0);
total=0;
perm(p,n,x,0);
return 0;
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(2)<<p[i];
cout<<endl;
}
void perm(int *p,int n,int x,int k)
{
if(k==n) //n个排列元素都已选定
output(p,n); //输出
else
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
if(p[j]!=x && p[j]==i) //检查除了x以外的元素是否重复
break; //退出循环
if(j==k) //除了x其余元素不重复
{
p[k]=i; //p[k]取值i
perm(p,n,x,k+1); //继续选取下一个元素
}
}
}
如果可还指定了可重复的元素的个数,则要在输出排列之前,检查该元素重复的次数,符合要求的排列就输出。
#include <iostream>
#include <iomanip>
using namespace std;
void perm(int*,int,int,int);
void output(int*,int);
int total;
int main(void)
{
int n=5,x=3,s,*p; //x是指定可重复的元素,s是重复次数
p=new int
;
fill_n(p,n,0);
total=0;
perm(p,n,x,s,0);
return 0;
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(2)<<p[i];
cout<<endl;
}
void perm(int *p,int n,int x,int k)
{
if(k==n) //n个排列元素都已选定
{
for(int i=0,t=0;i<n;i++) //计算指定元素的重复次数
if(p[i]==x)
t++;
if(t==s) //如果符合要求
output(p,n); //输出
}
else
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
if(p[j]!=x && p[j]==i) //检查除了x外的元素是否发生重复
break;
if(j==k) //除了x外的元素不重复
{
p[k]=i; //p[k]取值i
perm(p,n,x,k+1); //继续选取下一个元素
}
}
}
注意,对重复元素重复次数的检查是在不同位置进行的,原因是重复元素的重复次数只有在排列的元素全部选定,才能正确计算。
按照类似的方法,可以实现更复杂的排列。
现在来看一看产生各种特殊排列的算法。
递归形式的回溯算法如下:
//全排列(递归)
//输入:排列元素个数n
//输出:n个元素的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n)
{
p=new int
;
total=0;
backdate(p,n);
}
return 0;
}
void backdate(int *p,int n,int k) //递归生成排列
{
if(k==n) //如果排列的n个元素的值都已选定
output(p,n); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1到n中为p[k]选取一个值i
{
for(int j=0;j<k;j++) //所选的i是否与已选元素的值重复
if(p[j]==i)
break;
if(j==k) //不重复
{
p[k]=i; //选定
backdate(p,n,k+1); //继续为下一个元素p[k+1]选取值
}
}
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
生成n个元素的全排列过程中,有两个必要的条件:第一,n个排列元素的之都要选定,第二,n个排列元素要互不相同。既然如此,只要有其中一个条件发生变化,或两个条件都改变,所生成的排列就不再是全排列了。因此,可以从特殊排列的要求出发,改变排列元素的检验条件,就可以生成所需要的排列。
一.从n个不同元素中区m(m≤n)个的排列
在n个不同元素中,任选m个元素的排列可称为部分排列或选排列。显然,m≤n。如果m=n,那么就是全排列,所以,如果不做特别说明,可以认为m<n。
在n个元素的全排列中,每次所选取的元素个数必须要达到元素总数n,才输出一个排列。那么,现在只要须选定m个元素,就输出一个排列,所得的排列就是一个n个元素中选取m个元素的排列了。
以回溯法的递归程序为例,只要将代码按照代码中的黑体部分改变即可。
//n个元素取m个的排列(递归)
//输入:元素总数n,排列元素个数m
//输出:n个元素取m个的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,m,*p;
while(cin>>n>>m)
{
p=new int
;
total=0;
backdate(p,n,m,0);
}
return 0;
}
void backdate(int *p,int n,int m,int k) //递归生成排列
{
if(k==m) //如果排列中有m个元素的值被选定
output(p,m); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1 到n中为元素p[k]选取一个值
{
for(int j=0;j<k;j++) //所选的值i是否与已选元素的值重复
if(p[j]==i)
break;
if(j==k) //不重复
{
p[k]=i; //p[k]取值i
backdate(p,n,m,k+1); //继续为下一个元素选取值
}
}
}
4000
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
二.允许元素重复的全排列
一般情形,全排列中所有的元素都不相同。如果允许排列的元素重复,那就是允许可重复的排列。对于允许重复的全排列,只需要将生成全排列的算法中,将检验排列元素重复的代码去掉即可。
//元素可重复全排列(递归)
//输入:排列元素个数n
//输出:n个元素可重复的全体排列
#include<iostream>
#include <iomanip>
using namespace std;
void perm(int *,int);
void output(int *,int);
int total;
int main(void)
{
freopen("in.dat","r",stdin);
int n,*p;
while(cin>>n>>m)
{
p=new int
;
total=0;
backdate(p,n,0);
}
return 0;
}
void backdate(int *p,int n,int k) //递归生成排列
{
if(k==n) //如果排列的n个元素的值都已选定
output(p,n); //输出排列
else //否则
for(int i=1;i<=n;i++) //从1 到n中为元素p[k]选取一个值
{
// for(int j=0;j<k;j++) //所选的值i是否与已选元素的值重复
// if(p[j]==i)
// break;
// if(j==k) //不重复
{
p[k]=i; //p[k]取值i
backdate(p,n,k+1); //继续为下一个元素选取值
}
}
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(3)<<p[i];
cout<<endl;
}
三.指定元素可重复的全排列
如果在n个元素的全排列中,有一个元素可重复,而其余的元素不重复,生成这样的全排列。
在生成全排列的过程中,对每一个排列的元素,都要检验他是否与前面选出的元素重复,以此来保证每一个元素不会重复出现。既然如此,在要求某一个元素可以重复的情形,可以不对该元素进行重复出现的检验,它就会重复出现在排列中了。
//指定元素可重复的全排列
//输入:元素个数n,指定可重复元素x
//输出:要求的排列
#include <iostream>
#include <iomanip>
using namespace std;
void perm(int*,int,int,int);
void output(int*,int);
int total;
int main(void)
{
int n=5,x=3,*p; //x是指定可重复的元素
p=new int
;
fill_n(p,n,0);
total=0;
perm(p,n,x,0);
return 0;
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(2)<<p[i];
cout<<endl;
}
void perm(int *p,int n,int x,int k)
{
if(k==n) //n个排列元素都已选定
output(p,n); //输出
else
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
if(p[j]!=x && p[j]==i) //检查除了x以外的元素是否重复
break; //退出循环
if(j==k) //除了x其余元素不重复
{
p[k]=i; //p[k]取值i
perm(p,n,x,k+1); //继续选取下一个元素
}
}
}
如果可还指定了可重复的元素的个数,则要在输出排列之前,检查该元素重复的次数,符合要求的排列就输出。
#include <iostream>
#include <iomanip>
using namespace std;
void perm(int*,int,int,int);
void output(int*,int);
int total;
int main(void)
{
int n=5,x=3,s,*p; //x是指定可重复的元素,s是重复次数
p=new int
;
fill_n(p,n,0);
total=0;
perm(p,n,x,s,0);
return 0;
}
void output(int *p,int n)
{
cout<<setw(5)<<++total<<": ";
for(int i=0;i<n;i++)
cout<<setw(2)<<p[i];
cout<<endl;
}
void perm(int *p,int n,int x,int k)
{
if(k==n) //n个排列元素都已选定
{
for(int i=0,t=0;i<n;i++) //计算指定元素的重复次数
if(p[i]==x)
t++;
if(t==s) //如果符合要求
output(p,n); //输出
}
else
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
if(p[j]!=x && p[j]==i) //检查除了x外的元素是否发生重复
break;
if(j==k) //除了x外的元素不重复
{
p[k]=i; //p[k]取值i
perm(p,n,x,k+1); //继续选取下一个元素
}
}
}
注意,对重复元素重复次数的检查是在不同位置进行的,原因是重复元素的重复次数只有在排列的元素全部选定,才能正确计算。
按照类似的方法,可以实现更复杂的排列。
相关文章推荐
- Swift中文教程(一)基础数据类型
- CodeForces 606A-A. Magic Spheres【模拟】
- java发邮件(1)
- TCP连接的建立(二)
- JAVA线程同步辅助类Exchanger-交换
- 对一个多态例题学习的一些思考
- cocos2d-x 卓上改变工程图标的步骤
- 用消息队列和消息应用状态表来消除分布式事务
- 免费午餐已结束:并发成为软件的基本转向
- UICollectionView
- MFC学习(22)MFC常用类:CFile文件操作类
- Jenkins学习笔记(一)
- Gradle for Android
- [FAQ17469]打开自动调节背光功能后,手动调节背光到最小,屏幕完全变黑
- NSUserDefaults  的用法
- gstreamer 捕获图像+存储示例代码
- 最近碰到了一个病毒木马:virus.win32.ramnit.B
- 自动刷闪存脚本闪存
- iOS开发 关于iBeacon的一些记录
- Lua 5.1 参考手册