八皇后问题
将n个皇后放置在n*n的国际象棋棋盘上,其中没有任何两个皇后处于同一行,同一列或者同一对角线上,以使得的它们不能相互攻击。ios
问题分析
若是这样表示的话,那么判断条件应该如何表示呢?算法
回溯法
当把问题分红了若干步骤并递归求解时,若是当前步骤没有合理的选择时,则函数将返回上一级递归调用,这种现象叫回溯。正是由于这个缘由,递归枚举算法经常被称为回溯法 数组
源码
#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皇后问题,看一看有没有什么规律或者快速解法呢?性能