今天模拟赛最后一题暴力骗分没骗到,特此下定决心搞懂全排列
1.全排列的定义和公式:
从n个数中选取m(m<=n)个数按照一定的顺序进行排成一个列,叫作从n个元素中取m个元素的一个排列。由排列的定义,显然不同的顺序是一个不同的排列。从n个元素中取m个元素的所有排列的个数,称为排列数。从n个元素取出n个元素的一个排列,称为一个全排列。全排列的排列数公式为n!,通过乘法原理可以得到。
2.时间复杂度:
n个数(字符、对象)的全排列一共有n!种,所以全排列算法至少时间O(n!)
的。如果要对全排列进行输出,那么输出的时间要O(n∗n!)
,因为每一个排列都有n个数据。所以实际上,全排列算法对大型的数据是无法处理的,而一般情况下也不会要求我们去遍历一个大型数据的全排列。
# 例题luogu全排列问题
法1:STL大法好!
#include<bits/stdc++.h> #define rep(a,b,c) for(int a=b;a<=c;a++) using namespace std; int x[11]; int n; int main() { scanf("%d",&n); rep(i,1,n) { x[i]=i, printf(" %d",i); } while(next_permutation(x+1,x+1+n)) { printf("\n"); rep(i,1,n) printf(" %d",x[i]); } return 0; }
法2 dfs
#include<bits/stdc++.h> using namespace std; int n; int ans[15];//保存当前的方案 int use[15]; void dfs(int x)//X表示当前搜索到那个数 { if(x>n) {//如果N位都搜索完了,就输出方案并返回 for(int i=1;i<=n;i++) printf("%5d",ans[i]); printf("\n"); return; } for(int i=1;i<=n;i++)//从小到大枚举 if(!use[i]) { ans[x]=i;//保存到方案中 use[i]=1; dfs(x+1); use[i]=0; } } int main() { scanf("%d",&n); dfs(1); }
法3 状压dp
震惊!蒟蒻复制了大佬的题解!
#include<bits/stdc++.h> using namespace std; //PS:这里就不用标记数组啦!s就相当于是标记数组了呢。) int n,lg[1030],ans[10]; void dfs(int i,int s)//在dfs里加上一个形参s,是状态压缩的二进制数,1代表当前位置可以搜,反之是0。 { if(i>n) { for(int p=1;p<=n;p++) printf("%5d",ans[p]); return; } for(int ss=s;ss>0;ss-=ss&(-ss))//改一下递归部分的for(创建一个临时变量ss,替代当前一层的s;条件是s不为0;去掉ss的最后一位;) { int temp=ss&(-ss);//因为懒,就创建了一个临时变量。普及一下:一个数a求它二进制数的最右面一位1,就这么求:a&(-a) ans[i]=lg[temp];//因为状态压缩的每一位的数值就是2的(那一位)位数次方,所以lg数组起到了把位数上的值转变成位数的作用。 dfs(i+1,s-temp);//这里递归时s减掉最后一位就行了。 } } int main() { scanf("%d",&n); lg[1]=1; for(int i=2;i<=n;i++) lg[1<<(i-1)]=i;//创建一个数组,下标是二的n次方的位置里存的是n;1<<几就是2的几次方。 //(举个例子:下标是32的位置里存的是5,下标是1024的位置存的是10) dfs(1,(1<<n)-1);//第一次递归时所有位置都没搜 return 0; }