全排列问题与n皇后问题

全排列问题

基本思想:

从递归的角度考虑,将“输出从1~n的全排列”分为若干个子问题:“输出以1为开头的全排列”,“输出以2为开头的全排列”…… 于是不妨设定一个数组P,用来存放当前排列;再设定一个散列数组hashTable,其中hashTable[x]当整数x已经在数组P中时为true。

递归边界:当index达到n+1时,说明P的第1~n位都已经填好了,把数组P输出,表示生成了一个排列,然后直接return即可。

基本代码:n=3

#include
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
#include
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
#include
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;
}   

你可能感兴趣的:(算法)