八皇后问题之回溯法

八皇后问题之回溯法

八皇后问题

将n个皇后放置在n*n的国际象棋棋盘上,其中没有任何两个皇后处于同一行,同一列或者同一对角线上,以使得的它们不能相互攻击。ios

问题分析
  • 最简答的思路是把问题转化为“从64个格子中选一个子集”,使得“子集中刚好有8个格子,且任意选出两个格子都不在同一行,同一列或者赞成对角线上”。这刚好是子集枚举问题。然而,64个格子的子集有2^64个,太大了,则并非一个很好的模型。
  • 咱们把思路转化为“从64个格子中选出8个格子”,这是一个明显的组合问题,根据排列组合,有C8 64=4.426*10^9种结果,虽然比第一种要好,可是性能依然不够好。
  • 最后咱们经过思考,能不能将它装换为一维问题:由于每行每列各放置一个皇后,若是用C[X]表示第X行皇后的列编号,则问题会变成全排列生成问题,而0-7的排列一共只有8!=40320个,枚举的次数不会超过这个值。

若是这样表示的话,那么判断条件应该如何表示呢?算法

  • 咱们能够先用二维数组表示一个八宫格,假设每一个格子的坐标(x,y)是二维数组的下标,那么格子(x,y)的y-x的值就可以标识出租对角线,全部的主对角线的y-x的值是相等的,同理格子(x,y)的x+y值就能表示出副对角线,那么全部的问题就都解决了。
回溯法

当把问题分红了若干步骤并递归求解时,若是当前步骤没有合理的选择时,则函数将返回上一级递归调用,这种现象叫回溯。正是由于这个缘由,递归枚举算法经常被称为回溯法 数组

源码
#include<iostream>
#include<stdio.h>
using namespace std;

int n = 8; 
int c[8]={0};
int count = 0;
void search(int cur);

int main(){
    search(0);
} 

void print(){
    printf("\n-----------------\n");
    for(int i = 0; i < 8; i++){
        for(int j = 0; j < 8; j++){
            if(c[i] == j){
                printf("|*");
            }else{
                printf("| ");
            }
        }
        printf("|\n-----------------\n");
    }
}

void search(int cur){
    if(cur == n){
        print();
        printf("\n");
    }else{
        for(int i = 0; i < n; i++){
            c[cur] = i;
            int ok = 1; 
            for(int j = 0; j < cur; j++){
                if(c[cur] == c[j] || j - c[j] == cur - c[cur] || j + c[j] == cur + c[cur]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                search(cur+1);
            }
        }
    }
}
算法改进

上述算法的枚举的结点数适合很难减小了,可是程序的效率能够继续提升,利用二维数组直接判断当前尝试的皇后所在的列和两个对角线是否已有其余的皇后,注意的问题是,主对角线的表示y-x可能为负值,存取时要加上n。函数

void searchs(int cur){
    if(cur == n){
        count++;
    }else{
        for(int i = 0; i < n; i++){
            if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]){
                c[cur]=i;
                vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;
                searchs(cur + 1);
                vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;
            }
        }
    }
}

上述程序有个极其关键的地方:vis数组的使用。它表示的含义是已经放置的换后占据了哪些列,主对角线和副对角线。通常的,若是在回溯法中修改了辅助全局变量,则通常要把它们及时恢复原状,有多个地方修改,每一个地方都要恢复原有的值。
有兴趣的话,能够研究一下n皇后问题,看一看有没有什么规律或者快速解法呢?性能

相关文章
相关标签/搜索