【学习笔记】全排列

今天模拟赛最后一题暴力骗分没骗到,特此下定决心搞懂全排列

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;
}
posted @ 2019-10-08 12:07  设计涉及社稷  阅读(246)  评论(0编辑  收藏  举报