首页 > 技术文章 > 回溯法 | n皇后问题

TQCAI 2017-10-12 15:26 原文

今早上看了一篇英语阅读之后,莫名有些空虚寂寞冷。拿出算法书,研读回溯法。我觉得n皇后问题完全可以用暴力方式,即先对n个数进行全排列,得到所有结果的下标组合,问题规模为n!。

全排列花了比较久的时间才编写出来。主要是没有找对思路。最终我想到了递归,即对4个数进行全排列可以化为把【对3个数进行了全排列】的结果拿出来,在下标为1-4的位置上各插上一个数,一次类推。于是我编写了全排列类:

 1 //全排列
 2 class Arrangment{
 3     int[][]ans;
 4     Arrangment(){}
 5     Arrangment(int[] nums){
 6         ans=createA(nums.length,nums);
 7     }
 8     void printNums(int[][] nums){
 9         int row=nums.length;
10         int col=nums[0].length;
11         int i,j;
12         for(i=0;i<row;i++){
13             for(j=0;j<col;j++)
14                 System.out.print(nums[i][j]+" ");
15             System.out.print("\n");
16         }
17     }
18      int[][]createA(int rank,int []nums){
19         int[][] re;
20         if(rank>1){
21             int[][] pre=createA(rank-1,nums);
22             int row=pre.length;
23             int col=nums.length;
24             re=new int[row*rank][rank];
25             int index=0;
26             int i,j,k,m;
27             for(i=0;i<rank;i++){
28                 for(j=0;j<row;j++){
29                     for(k=0,m=0;k<rank  ;k++){
30                         if(k==i){//如果列下标等于i(在0~rank)中循环
31                             re[index][k]=nums[rank-1];
32                             int a;
33                             a=0;
34                         }else{
35                             re[index][k]=pre[j][m];
36                             m++;
37                         }
38                     }
39                     index++;
40                 }
41             }
42 
43         }
44         else{
45             re=new int[1][1];
46             re[0][0]=nums[0];
47         }
48         return re;
49     }
50     private int factorial(int n){
51         int re=1;
52         while(n>=1)
53             re*=(n--);
54         return re;
55     }
56 }

使用这个全排列类,就可以构造所有的问题的求解空间了。

暴力求解算法:

 1 class N_Queens{
 2     int n;
 3     N_Queens(int inN){
 4         n=inN;
 5     }
 6     List<int[]> solved =new ArrayList<int[]>();
 7     void solveByBrutal(){//穷举求解
 8 
 9     boolean isComplied(int[] nums,int len){
10         int i,j;
11         for(i=0;i<len-1;i++)
12             for(j=i+1;j<len;j++){
13                 int a=Math.abs(i-j);
14                 int b=Math.abs(nums[i]-nums[j]);
15                 if(a==b || b==0)
16                     return false;
17             }
18         return true;
19     }
20 }

然后我开始研究怎么使用【回溯法】进行解题。我看了严奶奶书上的伪代码,以及树形结构实例,感触颇深。原来我以为这个问题和8数码问题一样需要进行树的按层遍历,已经使用队列结构的,但是他既然使用了一种递归的思想,并且是深度优先的。用递归的入栈出栈,在【剪掉枝丫】(问题不可解)和【找到一个解】之后,自动的求下一个解。

回溯法代码:

 1 class N_Queens{
 2     int n;
 3     N_Queens(int inN){
 4         n=inN;
 5     }
 6     List<int[]> solved =new ArrayList<int[]>();
 7     void solveByBackTrace(){
 8         int[] nums=null;
 9         trial(nums,0);
10     }
11     void trial(int [] nums,int i){//棋盘上的前i行已经放置了【符合条件】的棋子
12         if(i<n){
13             int j,k;
14             //第i行进行放置
15             for(j=0;j<n;j++){//在第j列上进行遍历
16                 int[] node=new int[n];//新建结点
17                 for(k=0;k<i;k++)
18                     node[k]=nums[k];//拷贝父结点
19                 node[i]=j;//第i行第j列上放上一个皇后
20                 if(isComplied(node,i+1)){//符合条件
21                     trial(node,i+1);//扩展它的子节点
22                 }
23             }
24         }else{
25             solved.add(nums);
26         }
27     }
28 
29     boolean isComplied(int[] nums,int len){
30         int i,j;
31         for(i=0;i<len-1;i++)
32             for(j=i+1;j<len;j++){
33                 int a=Math.abs(i-j);
34                 int b=Math.abs(nums[i]-nums[j]);
35                 if(a==b || b==0)
36                     return false;
37             }
38         return true;
39     }
40 }

完整代码:

  1 import java.util.*;
  2 
  3 
  4 public class Main {
  5 
  6     public static void main(String[] args) {
  7         while(true){
  8             System.out.print("请输入皇后的数目:");
  9             Scanner scan=new Scanner(System.in);
 10             int n=scan.nextInt();
 11             N_Queens problem=new N_Queens(n);
 12             int []nums={1,2,3};
 13             problem.isComplied(nums, 2);
 14             problem.solveByBrutal();
 15             problem.PrintChecker();
 16         }
 17     }
 18 
 19 }
 20 
 21 //全排列
 22 class Arrangment{
 23     int[][]ans;
 24     Arrangment(){}
 25     Arrangment(int[] nums){
 26         ans=createA(nums.length,nums);
 27     }
 28     void printNums(int[][] nums){
 29         int row=nums.length;
 30         int col=nums[0].length;
 31         int i,j;
 32         for(i=0;i<row;i++){
 33             for(j=0;j<col;j++)
 34                 System.out.print(nums[i][j]+" ");
 35             System.out.print("\n");
 36         }
 37     }
 38      int[][]createA(int rank,int []nums){
 39         int[][] re;
 40         if(rank>1){
 41             int[][] pre=createA(rank-1,nums);
 42             int row=pre.length;
 43             int col=nums.length;
 44             re=new int[row*rank][rank];
 45             int index=0;
 46             int i,j,k,m;
 47             for(i=0;i<rank;i++){
 48                 for(j=0;j<row;j++){
 49                     for(k=0,m=0;k<rank  ;k++){
 50                         if(k==i){//如果列下标等于i(在0~rank)中循环
 51                             re[index][k]=nums[rank-1];
 52                             int a;
 53                             a=0;
 54                         }else{
 55                             re[index][k]=pre[j][m];
 56                             m++;
 57                         }
 58                     }
 59                     index++;
 60                 }
 61             }
 62 
 63         }
 64         else{
 65             re=new int[1][1];
 66             re[0][0]=nums[0];
 67         }
 68         return re;
 69     }
 70     private int factorial(int n){
 71         int re=1;
 72         while(n>=1)
 73             re*=(n--);
 74         return re;
 75     }
 76 }
 77 
 78 class N_Queens{
 79     int n;
 80     N_Queens(int inN){
 81         n=inN;
 82     }
 83     List<int[]> solved =new ArrayList<int[]>();
 84     void solveByBrutal(){//穷举求解
 85         int i;
 86         int indexs[]=new int[n];
 87         for(i=0;i<n;i++) indexs[i]=i;
 88         Arrangment solve=new Arrangment(indexs);
 89         int[][] solceSpace=solve.ans;//构造所有解空间
 90         solved.clear();
 91         for(i=0;i<solceSpace.length;i++){
 92             if(isComplied(solceSpace[i],n))
 93                 solved.add(solceSpace[i]);
 94         }
 95         int a;
 96         a=0;
 97     }
 98     void solveByBackTrace(){
 99         int[] nums=null;
100         trial(nums,0);
101     }
102     void trial(int [] nums,int i){//棋盘上的前i行已经放置了【符合条件】的棋子
103         if(i<n){
104             int j,k;
105             //第i行进行放置
106             for(j=0;j<n;j++){//在第j列上进行遍历
107                 int[] node=new int[n];//新建结点
108                 for(k=0;k<i;k++)
109                     node[k]=nums[k];//拷贝父结点
110                 node[i]=j;//第i行第j列上放上一个皇后
111                 if(isComplied(node,i+1)){//符合条件
112                     trial(node,i+1);//扩展它的子节点
113                 }
114             }
115         }else{
116             solved.add(nums);
117         }
118     }
119 
120     boolean isComplied(int[] nums,int len){
121         int i,j;
122         for(i=0;i<len-1;i++)
123             for(j=i+1;j<len;j++){
124                 int a=Math.abs(i-j);
125                 int b=Math.abs(nums[i]-nums[j]);
126                 if(a==b || b==0)
127                     return false;
128             }
129         return true;
130     }
131     
132     public String toString(){
133         int i,j;
134         String str=new String("");
135         for(i=0;i<solved.size();i++){
136             int [] out=solved.get(i);
137             for(j=0;j<out.length;j++){
138                 str+=out[j];
139                 str+=" ";
140             }
141             str+="\n";
142         }
143         return str;
144     }
145     void PrintChecker(){
146         int i,j,k;
147         String out=new String();
148         for(i=0;i<solved.size();i++){
149             int[] nums=solved.get(i);
150             for(j=0;j<nums.length;j++){
151                 int l=nums[j];
152                 int r=nums.length-1-nums[j];
153                 for(k=0;k<l;k++) out+="□";
154                 out+="■";
155                 for(k=0;k<r;k++) out+="□";
156                 out+="\n";
157             }
158             out+="\n";
159         }
160         System.out.print(out);
161     }
162 }
View Code

输出结果:

4皇后:

8皇后:

 

 10皇后:

 

 

推荐阅读