今天模拟赛最后一题暴力骗分没骗到,特此下定决心搞懂全排列
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("
");
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("
");
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;
}