当时就觉得回溯法学的不是很好,在处理dp多状态问题前,温习一遍回溯法:把待求解的问题分成不太多的步骤,每个步骤只有不太多的选择,就可以考虑回溯法。
普通八皇后思路:从64格子中选8个格子,枚举的话一共有$C^{8}_{64}=4.426X10^{9}$解法,不爆才怪。
观察基本性质,每一行只能有一个皇后,所以最多只有$8!$种枚举可能。在实际操作的时候,状态树伸展肯定比这个值还要小,因为和之前的状态已经发生冲突,递归函数就不会再调用自身,而是返回上一层调用,这种现象被称为回溯(backtracking)。 回溯的关键在于回到上层调用时,要把改过去的状态改回来!否则原来状态被改变就不是原状态了。
代码:
1 int tot; 2 bool attacked[3][16];// 0 1 2分别表示是否被前面的皇后占据了列,左斜线,右斜线, 后面表示棋盘长度(8皇后有15斜) 3 4 void search(int a){ //逐行搜索 5 if(a==n){ 6 tot++; //递归边界,到了这里表示已经圆满完成 7 } 8 else{ 9 for(int i=0;i<n;i++){ //对于这一行,判断这一列能不能放 10 if(!attacked[0][i]&&!attacked[1][a+i]&&!attacked[2][n-a+i]){ 11 //尝试放置 12 // cell[a] = i; //记录这行放置的列,用于打印 13 attacked[0][i] = true; 14 attacked[1][a+i] = true; 15 attacked[2][n-a+i] = true; 16 search(a+1); 17 //调用完毕后一定要把状态改回来! 18 //也就是说改变的状态是给之后的子状态用的,而不能影响同层其他结构 19 attacked[0][i] = false; 20 attacked[1][a+i] = false; 21 attacked[2][n-a+i] = false; 22 } 23 } 24 } 25 } 26 27 int main(){ 28 cin>>n; //棋盘大小 29 tot = 0; 30 memset(attacked,0,sizeof(attacked)); 31 32 search(0);//从第0行开始 33 cout<<tot; 34 }
解题代码:
1 // 2 // main.cpp 3 // 8 QUEENS 4 // 5 // Created by Yanbin GONG on 12/3/2018. 6 // Copyright © 2018 Yanbin GONG. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string> 12 #include <string.h> 13 #define N 8 14 15 int n; 16 bool vis[3][2*N]; 17 int r, c, kase; 18 int ans[N]; 19 20 void search(int cur) 21 { 22 if (cur == N) 23 { 24 printf("%2d ", ++kase); 25 for (int i = 0; i < N; i++) 26 printf("%d%s", ans[i]+1, (i==N-1) ? " " : " "); 27 return; 28 } 29 if (cur == c) search(cur+1); 30 else 31 { 32 for (int i = 0; i < N; i++) 33 if (!vis[0][i] && !vis[1][cur-i+N] && !vis[2][cur+i]) 34 { 35 ans[cur] = i; 36 vis[0][i] = vis[1][cur-i+N] = vis[2][cur+i] = 1; 37 search(cur+1); 38 vis[0][i] = vis[1][cur-i+N] = vis[2][cur+i] = 0; 39 } 40 } 41 } 42 43 int main() 44 { 45 scanf("%d", &n); 46 while (n--) 47 { 48 scanf("%d%d", &r, &c); 49 r--; 50 c--; 51 memset(vis, 0, sizeof(vis)); 52 vis[0][r] = 1; 53 vis[1][c-r+N] = 1; 54 vis[2][c+r] = 1; 55 ans[c] = r; 56 kase = 0; 57 printf("SOLN COLUMN "); 58 printf(" # 1 2 3 4 5 6 7 8 "); 59 search(0); 60 if (n) printf(" "); 61 } 62 return 0; 63 }