全排列问题
基本思想:
从递归的角度考虑,将“输出从1~n的全排列”分为若干个子问题:“输出以1为开头的全排列”,“输出以2为开头的全排列”…… 于是不妨设定一个数组P,用来存放当前排列;再设定一个散列数组hashTable,其中hashTable[x]当整数x已经在数组P中时为true。
递归边界:当index达到n+1时,说明P的第1~n位都已经填好了,把数组P输出,表示生成了一个排列,然后直接return即可。
基本代码:n=3
#include<cstdio>
const int maxn = 11;
//p为当前排列,hashTable记录整数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[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(){
n = 3;
generateP(1);
return 0;
}
n皇后问题
问题描述:在一个n*n的国际象棋棋盘上放置n个皇后,使得这n个皇后两两均不在同一行、同一列、同一对角线上。
递归法方案:
基本思想与全排列问题类似,只不过在达到递归边界的情况下加了判断是否存在同一列的方法。
代码如下:
#include<cstdio>
#include<cmath>
const int maxn = 11;
int n, P[maxn], hashTable[maxn] = { false };
int count = 0;
int 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; //不合法
}
}
}
if (flag)
{
for (int i = 1; i <= n; i++)
{
printf("%d ", P[i]);
}
printf("\n");
count++;
}
return count;
}
for (int x = 1; x <= n; x++)
{
if (hashTable[x] == false)
{
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
int main(){
n = 5;
printf("共有%d种方法\n",generateP(1));
return 0;
}
回溯法:
基本思想:上一个方法是相当暴力的,可以发现,可能存在放置一部分皇后就出现了不合法的情况,所以就没有必要接着往下进行递归了。就产生了回溯法。
代码:
#include<cstdio>
#include<cmath>
const int maxn = 11;
int n, P[maxn], hashTable[maxn] = { false };
int count = 0;
int generateP(int index){
if (index == n + 1){
for (int i = 1; i <=n; i++) //能达到这里一定是合法的
{
printf("%d ", P[i]);
}
printf("\n");
count++;
return count;
}
for (int x = 1; x <=n; x++)
{
if (hashTable[x]==false)
{
bool flag = true; //flag表示当前皇后不会跟之前的皇后冲突
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)
{
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
}
int main(){
n = 5;
int i = generateP(1);
printf("%d\n",i);
return 0;
}