1、全排列简介
从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫全排列。
公式:全排列数f(n)=n!(定义0!=1),
如 1,2,3 三个元素的全排列为:
1,2,3 、1,3,2、2,1,3、2,3,1、3,1,2、3,2,1 共321=6种。
2、小白求全排列
解释:
如下图的代码,运用continue来剪枝尽可能优化。10个数做全排列花费时间477毫秒,不同的电脑花费的时间不同。
3、DFS算法求全排列
解释:
采用DFS(深度优先搜索)算法来求全排列,巧妙运用锁来剪枝,还有最重要的是他能保证字典序。DFS的用法非常广泛,例如图的遍历等等。10个数做全排列花费时间306毫秒,不同的电脑花费的时间不同。
如下图中代码基本都有注释,如果没有看懂,需要补递归的知识。
4、递归交换法求全排列
解释:
采用递归与还原的方式来求全排列,效率非常高,基本上没有多余的步骤,但是他不能保证字典序。如果你发现有比这个效率高的,请在后台私信我。10个数做全排列花费时间54毫秒,不同的电脑花费的时间不同。
如下图中代码基本都有注释,如果没有看懂,需要补递归的知识。
5、全排列算法实战题目
1、九宫幻方
问题描述
小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将1~9不重复的填入一个3*3的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。
三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:“二四为肩,六八为足,左三右七,戴九履一,五居其中”,通过这样的一句口诀就能够非常完美的构造出一个九宫格来。
4 9 2
3 5 7
8 1 6
有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小明准备将一个三阶幻方(不一定是上图中的那个)中的一些数抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一个解。
而你呢,也被小明交付了同样的任务,但是不同的是,你需要写一个程序~
输入格式
输入仅包含单组测试数据。
每组测试数据为一个3*3的矩阵,其中为0的部分表示被小明抹去的部分。
对于100%的数据,满足给出的矩阵至少能还原出一组可行的三阶幻方。
输出格式
如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出“Too Many”(不包含引号)。
样例输入
0 7 2
0 5 0
0 3 0
数据规模和约定
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms
import java.util.Scanner;
public class Main {
static Scanner sc;
static int count = 0;
static int[] b;
public static void main(String[] args) {
int[] a = {1,2,3,4,5,6,7,8,9};
b = new int[9];
sc = new Scanner(System.in);
for(int i=0;i<b.length;i++){
b[i] = sc.nextInt();
}
permutation(a, 0, a.length-1);
if(count==0){
System.out.println("Too Many");
}
}
static void permutation(int a[], int start, int end){
if(start==end){
if(judge(a)){//符合九宫幻方
boolean tempjudge = true;
for(int i=0;i<b.length;i++){
if(b[i]!=0){
if(b[i]!=a[i]){
tempjudge = false;
}
}
}
if(tempjudge){
System.out.println(a[0] +" "+ a[1] +" "+ a[2]);
System.out.println(a[3] +" "+ a[4] +" "+ a[5]);
System.out.println(a[6] +" "+ a[7] +" "+ a[8]);
count++;
}
}
}else{
for(int i=start;i<=end;i++){
a = swap(a, start, i);
permutation(a, start+1, end);
a = swap(a, start, i);
}
}
}
static int[] swap(int a[], int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
return a;
}
static boolean judge(int a[]){
//行
int temp1 = a[0] + a[1] + a[2];
int temp2 = a[3] + a[4] + a[5];
int temp3 = a[6] + a[7] + a[8];
if(temp1!=temp2||temp1!=temp3||temp2!=temp3) return false;
//列
temp1 = a[0] + a[3] + a[6];
temp2 = a[1] + a[4] + a[7];
temp3 = a[2] + a[5] + a[8];
if(temp1!=temp2||temp1!=temp3||temp2!=temp3) return false;
//对角线
temp1 = a[0] + a[4] + a[8];
temp2 = a[2] + a[4] + a[6];
if(temp1!=temp2) return false;
return true;
}
}
2、带分数
问题描述
100 可以表示为带分数的形式:100 = 3 + 69258 / 714。还可以表示为:100 = 82 + 3546 / 197。
注意特征:
带分数中,数字1~9分别出现且只出现一次(不包含0)。类似这样的带分数,100 有 11 种表示法。
输入格式
从标准输入读入一个正整数N (N<1000*1000)
输出格式
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
样例输入1
100
样例输出1
11
样例输入2
105
样例输出2
6
import java.util.Scanner;
public class Main {
static int count = 0;
static int N = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
//long start = System.currentTimeMillis();
int[] a = new int[]{1,2,3,4,5,6,7,8,9};
permu(a, 0, a.length-1);
System.out.println(count);
//System.out.println(System.currentTimeMillis()-start);
}
public static void permu(int[] a, int start, int end){
if(start==end){
out:
for(int i=0;i<7;i++){//对加号控制
for(int j=i+1;j<8;j++){//对除号控制
int x = cut(a, 0, i);//加号前的数
int y = cut(a, i+1, j);//加号后除号前的数
int z = cut(a, j+1, 8);//除号后的数
if(x>N){//如果加号前的数大于N就没必要算了
break out;
}
if(y<z||y%z!=0){//当后面的y z是小数的时候就没必要查找了
continue;
}
int result = x+y/z;//计算出结果
if(result==N){
count++;
//System.out.println(x+"+"+y+"/"+z);
}
}
}
}else{
for(int i=start;i<=end;i++){
a = swap(a, start, i);
permu(a, start+1, end);
a = swap(a, start, i);
}
}
}
public static int[] swap(int[] a, int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
return a;
}
public static int cut(int[] a, int start, int end){
int temp = 0;
for(int i=start;i<=end;i++){
temp = temp*10 + a[i];
}
return temp;
}
}