说明:这里的全排列是按字典序的.
以下给出从1到3的全排列代码:
#include<iostream>
#include<stdlib.h>
using namespace std;
const int maxn = 11;
//P为当前排列,hashTable记录整数x是否已经在P中
int p[maxn];// 必须申明为全局
bool hashTable[maxn] = { false };// 必须申明为全局
int n;// 必须申明为全局
//当前处理排列的第index号位
void generateP(int index)
{
if (index == n + 1)//递归边界:当index递归到n+1时,说明index已经从1至n递归完毕了
{
for (int i = 1; i <= n; i++)
{
cout << p[i];///输出当前排列
}
cout << endl;
return;
}
for (int x = 1; x <= n; x++)//枚举1至n,试图将x填入p[index]
{
if (hashTable[x] == false)//如果x不在p[0]至p[index-1]中,则
{
p[index] = x;//令p的第index位为x,即把x加入当前排列
hashTable[x] = true;//记x已在p中
generateP(index + 1);//处理排列的第index+1号位
hashTable[x] = false;//已处理完p[index]为x的子问题,还原状态
}
}
}
int main(void)
{
n = 3;//欲输出1至3的全排列
generateP(1);//从p[1]开始填
system("pause");
return 0;
}
注:其中generateP函数的代码十分重要,切记.
以下来讨论N皇后问题,其实N皇后问题输出值是解决方案个数,其实质就是全排列的应用.
1)暴力法(一般把不使用优化算法,直接用朴素算法来解决问题的做法称暴力法)
//第i列皇后的行号为P[i],第j列皇后行号为P[j].
int count = 0;
void generateP(int index)
{
if (index == n + 1)//递归边界,生成一个排列
{
bool flag = true;//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
{
flag = false;
}
}
}
if (flag) count++;//合法则加一
return;
}
for (int x = 1; x <= n; x++)
{
if (hashTable[x] == false)
{
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
2)回溯法(一般来说,如果在到达递归边界前的某层,由于一些事实导致已经不需要往任何一个子问题递归,就可以直接返回上一层)
void generateP(int index)
{
if (index == n + 1)//递归边界,生成一个合法方案
{
count++;//能达到这一定合法
return;
}
for (int x = 1; x <= n; x++)//第n行
{
if (hashTable[x] == false)//第n行还没有皇后
{
bool flag = true;//flag为true表示当前皇后不会和之前的皇后冲突
for (int pre = 1; pre < index; pre++)//遍历之前的皇后
{
//第index列皇后行号为x,第pre列皇后的行号为P[pre]
if (abs(index - pre) == abs(x - P[pre]))
{
flag = false;//与之前的皇后在一条对角线冲突
break;
}
}
if (flag)//如果可以把皇后放在第x行
{
P[index] = x;//令第index列皇后的行号为x
hashTable[x] = true;//第x行已被占用
generateP(index + 1);//递归处理第index+1行皇后
hashTable[x] = false;//递归完毕,还原第x行为未占用
}
}
}
}