全排列——内容整理

1.生成1-n的全排列:

代码:

#include<stdio.h>
int A[100];
void print_permutation(int n, int* A, int cur)
{
	int i, j;
	if(cur == n)// 递归边界;
	{
		for(i = 0; i < n; i++) printf("%d ", A[i]);
		printf("\n");
	}
	else 
	{
		for(i = 1; i <= n; i++)// 尝试在A[cur]中填各种整数i 
		{ 
			int ok = 1;
			for(j = 0; j < cur; j++)
			if(A[j] == i) ok = 0; // 如果i已经在A[0]~A[cur-1]出现过,则不能再选
			if(ok)
			{
				A[cur] = i;
				print_permutation(n, A, cur + 1); // 递归调用
			}//if
		}//for
	}//else
}
int main(void)
{
  print_permutation(4, A, 0); 
  return 0;
}

结果:
全排列——内容整理_第1张图片

优化一(增加一个标记数组):

void permutation(int cur,int n) 
{
  int i;
  if(cur == n)
  { // 递归边界
	  sum++;
    for(i = 1; i < n; i++) printf("%d ", a[i]);
    printf("\n");
  }
  else
	  for(i = 1; i < n; i++)
	  { 
		if(vis[i] == 0)
		{
			a[cur] = i;
			vis[i] = 1;//选
			permutation(cur + 1, n);
			vis[i] = 0;//不选
		}//if
	}//for
}
完整代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_N 10
int n;//共n个数
int rcd[MAX_N];//记录每个位置填的数 
int used[MAX_N];//标记每个数是否用过; 
int num[MAX_N];//存放输入的n个数; 
void full_permutation(int cur)
{
	int i;
	if(cur == n)
	{
		for(i = 0; i < n; i++)
		{
			printf("%d", rcd[i]);
		}
		printf("\n");
		return ;
	}
	for(i = 0; i < n; i++)//枚举所有的数(n个), 循环从开始  
	{
		if(!used[i])//若num[i]没有使用过,则标记为已使用 
		{
			used[i] = 1;
			rcd[cur] = num[i];//在cur位置上放上该数 
			full_permutation(cur + 1);//填下一个位置
			used[i] = 0;//清标记 
		}
	}
}
int main(void)
{
	scanf("%d", &n);
	for(int i = 0; i < n; i++)
	{
		scanf("%d",&num[i]);	
	}
	full_permutation(0);
	return 0;
}
//程序通过 used数组,标记数是否被用过,可以产生全排列,共n!种。 
//但是通过观察会发现,若输入的n个数有重复,那么在输出n!种排列
//中,必然存在重复的项。 


 
 

2.生成可重集的全排列:

若问题为:

输入数组P,并按字典序输出数组A各元素的所有全排列。

则需要对上述程序进行修改:

(1).把P添加到printf_permutation的参数列表中, 然后把代码中的if(A[j] == i) 和 A[cur] = i改成if(A[i] == P[i]) 和A[cur] == P[i];

(2).P的所有元素按照从小到大排序;

(3).对于处理1 1 1这种情况,统计A[0] ~A[cur - 1]中P[i]出现的次数c1,以及P数组中P[i]出现的次数c2.只要c1 < c2,就能调用递归;

(4).对于(3)中的那种情况,进行了上述处理后,只是有结果输出了,但是会输出27个1 1 1,遗漏倒是没有, 可是出现了重复。对此,我们应该知道,我们枚举的下标是应该不重复、不遗漏地取遍所有P[i]值,由于P数组已经排过序了,所以只需检查P的第一个元素和所有“与前一个元素不相同”的元素,即只需在for循环和后面的花括号之间加上if(!i || P[i] != P[i - 1])即可。

代码:

#include<stdio.h>
int P[100], A[100];
void print_permutation(int n, int* P, int* A, int cur)
{
	int i, j;
	if(cur == n)
	{
		for(i = 0; i < n; i++)
			printf("%d ", A[i]);
		printf("\n");
	}
	else
	{
		for(i = 0; i < n; i++)
			if(!i || P[i] != P[i-1])
			{
				int c1 = 0, c2 = 0;
				for(j = 0; j < cur; j++) if(A[j] == P[i]) c1++;
				for(j = 0; j < n; j++) if(P[i] == P[j]) c2++;
				if(c1 < c2)
				{
					A[cur] = P[i];
					print_permutation(n, P, A, cur+1);
				}//if
			}//if
	}//else
}
int main(void)
{
	int i, n;
	scanf("%d", &n);
	for(i = 0; i < n; i++)
		scanf("%d", &P[i]);
	print_permutation(n, P, A, 0);
	return 0;
}

结果:

全排列——内容整理_第2张图片


3.下一个排列:

这是枚举的另一个方法:从字典序最小排列开始,不停地调用“求下一个排列”的过程。

如何求下一个排列?

C++的STL中提供了一个库函数next_permutation。

代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
int main(void)
{
	int n, p[10];
	scanf("%d", &n);
	for(int i = 0; i < n; i++)
		scanf("%d", &p[i]);
	sort(p, p+n); // 排序,得到p的最小排列
	do
	{
		for(int i = 0; i < n; i++)
			printf("%d ", p[i]); // 输出排列p
		printf("\n");
	}while(next_permutation(p, p + n)); // 求下一个排列
  return 0;
}
结果:
全排列——内容整理_第3张图片

你可能感兴趣的:(全排列——内容整理)