全排列
问题描述
输入正整数n,输出所有由1-n数字组成的数列,例如,当n=3时,全排列有: < 1 , 2 , 3 > , < 1 , 3 , 2 > , < 2 , 1 , 3 > , < 2 , 3 , 1 > , < 3 , 1 , 2 > , < 3 , 2 , 1 > <1,2,3>,<1,3,2>,<2,1,3>, <2,3,1>, <3,1,2>,<3,2,1> <1,2,3>,<1,3,2>,<2,1,3>,<2,3,1>,<3,1,2>,<3,2,1>;
问题分析
- 从递归的角度考虑,可以划分为若干子问题:“以1开头的全排列”,“以2开头的全排列”…
- 设数组P[n]保存排列结果,数组hashTable[n]保存数字x是否已经在全排列中;
递归方式
- 先填位置为index的数字,然后填位置为index+1的数字(用hashTable来保证每次填入的数字不重复),填完index+1位置的数字后,然后将index位置的数字设置为为使用,来保证不同的数字都可以填在相同的位置;当index==n+1时,达到递归边界,将结果输出;(注:这里不太好理解,需要仔细揣摩 )
C++代码
#include<cstdio>
const int maxn = 11;
//P为当前排列,hasTable记录整数x是否已经在P中
int n,P[maxn],hashTable[maxn]={false};
//处理排列的第index号位
void generateP(int index){
if(index==n+1){//递归边界,已经处理完排列的1~n位
for(int i =1;i<=n;i++){
printf("%d",P[i]);//输出当前排列
}
printf("\n");
return;
}
for(int x=1;x<=n;x++){//枚举1~n,试图将x填入P[index]
if(hashTable[x] == false){//数字x是否已经用过,若未用过则使用
P[index]=x;//把x放在index处
hashTable[x]=true;//x已使用
generateP(index+1);//填下一个位置数字
hashTable[x]=false;//表示用x填完当前位置数字
}
}
}
int Full_Permutation(){
scanf("%d",&n);
generateP(1);
return 0;
}
从全排列到n皇后问题
我们可以发现n皇后问题和全排列问题的相似之处:由于规则限制,显然相同行和相同列只能有一个皇后,即可以把皇后的位置转化为一位数组P[x],其中下标x表示行,P[x]值表示列,那么问题就可以缩小转化为带限制条件的全排列问题;
与全排列的问题的区别
由于限制条件还有不能在同一对角线上,所以限制条件要加上任意两个皇后的所在行数之差的绝对值不能等于所在列数之差的绝对值,即 ∣ P [ m ] − P [ n ] ∣ ≠ m − n |P[m]-P[n]|≠m-n ∣P[m]−P[n]∤=m−n;
C++代码
#include<cstdio>
#include<cmath>
namespace eight_queens {
const int maxn = 20;
int n,P[maxn],_hashTable[maxn]= {false};
int count=0;
}
using namespace eight_queens;
void generate(int index) {
if(index==n+1) {
bool flag=true;
for(int i=1; i<=n; i++) {//这里判断是否在同一对角线
for(int j=i+1; j<=n; j++) {
if(abs(i-j)==abs(P[i]-P[j])) {
flag=false;
}
}
}
if(flag==true) {
count++;
}
return;
}
for(int x=1; x<=n; x++) {
if(_hashTable[x]==false) {
P[index]=x;
_hashTable[x]=true;
generate(index+1);
_hashTable[x]=false;
}
}
}
int eight_queens() {//运行时要改成main
scanf("%d",&n);
generate(1);
printf("%d\n",count);
return 0;
}