全排列问题与n皇后问题

来源:互联网 发布:win查看端口占用 编辑:程序博客网 时间:2024/05/22 15:02

全排列问题

基本思想:

从递归的角度考虑,将“输出从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;}