关键点分析提示:
①如何判断能否放置皇后?
答:如果仔细分析就知道,其实判断的范围就只有该皇后的前几行,比如这个图:
当你准备在第三行放四皇后的时候,你不需要管后面几行的情况,因为没有四皇后,也不需要管当前行的情况,因为一行只有一个皇后,所以判断的范围缩小,只需要在这个范围内没有皇后即可,如图:
如果你理解了这样的判断,那么如何判断,就可以参考我的代码里面的函数CanPut,if语句里面就给出了判断方法
②如何重新放置?
答:我们可以先假设这个位置能放置,具体到算法里面就是先把这个位置置为‘1’,然后再调用CanPut函数判断能否放,如果能,递归进入下一层,判断下一行的皇后摆放,并且当回溯回来的时候,要重新摆放,也就是‘1’要归0。那么这里我们就可以分析知道,无论是否可以Place,也就是无论是本来就不能摆放,还是摆放完后虽然可以当时回溯回来后又不能再摆放(因为要判断下一个位置),这个位置总是要归0的。所以为了使逻辑简单,不妨先假设能放,后面又取消这个位置,如此不至于留下皇后摆放的痕迹,给问题的处理带来困难。
③递归的出口在哪?
答:不难想到,递归求解N皇后问题不过是暴力破解,那么当所有的情况都试过了,自然递归的就结束了,具体讲,就是第一行的皇后已经没位置放了,递归就结束了。
以下是代码:
#include<stdio.h> #define Debug int count=0; void Queen(int n,int l); bool CanPut(int i,int j,int (*a)[8],int n); void Print(int (*a)[8],int n); int main() { printf("请输入棋盘的规模\n"); int n; scanf("%d",&n); Queen(n,0); if(count) printf("解的总数是:%d\n",count); else printf("无解\n"); return 1; } int a[8][8]={0}; void Queen(int n,int l) { if(l==n-1)//最后一行,递归出口 { for(int j=0;j<n;j++) { if(CanPut(l,j,a,n))//是否能放置皇后 { count++; a[l][j]=1;//标记位置 Print(a,n); a[l][j]=0;//取消这个位置 } } printf("\n"); } else//不是最后一行 { for(int j=0;j<n;j++) { a[l][j]=1;//假设这个位置可以放 if(CanPut(l,j,a,n))//如果可以,递归进入下一行 Queen(n,l+1); a[l][j]=0;//回溯回来后或者不可以放置皇后归0 } } return;//如果是if,则递归出口,如果是else,则回溯到上一行 } void Print(int (*a)[8],int n) {//本算法打印出N皇后的解 for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { printf("%d ",a[i][j]); } printf("\n"); } } bool CanPut(int i,int j,int (*a)[8],int n) {//本算法是判断该位置能否放皇后 int s=0; for(int k=i-1;k>=0;k--) { s++; if(a[k][j]==1 || j+s<n&&a[k][j+s]==1 || j-s>=0&&a[k][j-s]==1) return false; } return true; }