全排列
总提交:965 测试通过:228
描述
全排列的生成就是对于给定的字符集或数集,用有效的方法将所有可能的全排列无重复无遗漏地枚举出来。对给定的字符集中的字符规定一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后,或根据给定的数集中的大小关系,规定两个全排列的先后是从左到右逐个比较对应的数的大小,即依照字典序给出全排列。例如字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
给定n个整数,现请编程求它们所有的全排列。
输入
输入包括两个行,第一行给出正整数n( 0 < n <=8), 第二个是 n个整数(大小范围:[-10^4, 10^4])。
输出
按字典序输出这n个整数的全排列,每一行给出一个全排列。
样例输入
3
1 23 88
样例输出
1 23 88
1 88 23
23 1 88
23 88 1
88 1 23
88 23 1
题目来源
NUPT
分析:2种方法。
一种是字典序法(AC):(引用自百度~)
对给定的字符集中的字符规定了一个先后关系,在此基础上规定两个全排列的先后是从左到右逐个比较对应的字符的先后。 [例]字符集{1,2,3},较小的数字较先,这样按字典序生成的全排列是:123,132,213,231,312,321。
[注意] 一个全排列可看做一个字符串,字符串可有前缀、后缀。
1)生成给定全排列的下一个排列 所谓一个的下一个就是这一个与下一个之间没有其他的。这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
[例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的是987654321,从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。
//全排列——字典序法
#include<stdio.h>
#include<algorithm>
using namespace std;
#define SWAP(x, y) {int tmp=x; x=y; y=tmp;}
int n;
int a[8];
void invert(int pos) // pos到n-1逆序
{
int cnt = n-1-pos+1;
for(int i=0;i<cnt/2;i++)
SWAP(a[pos+i], a[n-1-i]);
}
void print()
{
for(int i=0;i<n-1;i++)
printf("%d ",a[i]);
printf("%d\n",a[n-1]);
}
void dictionary()
{
if(n == 1)
{
print();
return ;
}
sort(a, a+n);
int num = 1; // num种排列
for(int i=1;i<=n;i++)
num *= i;
num--;
print(); // 输出初始 小到大序列
while(num--)
{
int pos1 = 0;
int pos2 = 0;
//从n-2开始,找第一个比右边值小的位置pos1
for(int j=n-2;j>=0;j--)
{
if(a[j] < a[j+1])
{
pos1 = j;
break;
}
}
if(pos1 < 0 || pos1 >= n) return ;
//从n-1开始,找第一个比a[pos1]大的位置
for(int j=n-1;j>pos1;j--)
{
if(a[j] > a[pos1])
{
pos2 = j;
break;
}
}
SWAP(a[pos1], a[pos2]); // 交换
invert(pos1+1); // pos1+1 ~ n-1 逆序
print();
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
dictionary();
return 0;
}
第二种是递归枚举排列~ 较上一种字典排序比较简便,附AC代码
//全排列——递归算法——排序之后对下标(1~n)全排列
#include<iostream>
#include<algorithm>
using namespace std;
int arr[8], n;
int xb[8]; // 下标
int cmp(const void *x, const void *y)
{
return *(int *)x - *(int *)y;
}
void fun(int n, int cur)
{
if(cur == n) // 递归边界
{
for(int i=0;i<n-1;i++)
printf("%d ",arr[xb[i]-1]);
printf("%d\n",arr[xb[n-1]-1]);
}
else for(int i=1;i<=n;i++) // :) 在xb[cur]中填入各种整数i
{
int tag = 1;
for(int j=0;j<cur;j++)
if(xb[j] == i) tag = 0; // 如果i已经在xb[0]~xb[cur-1]中出现过,则不能再选
if(tag)
{
xb[cur] = i;
fun(n, cur+1);
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&arr[i]);
//sort(arr, arr+n);
qsort(arr, n, sizeof(arr[0]), cmp); // 必须先排序!
fun(n, 0);
return 0;
}