题目
分析一
本问题可以按照全排列的dfs来写。生成一个排列数,下标 i 对应第 i 行,其上的数字代表放哪一列。
代码
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int N = 20;//这里开到20 5 bool col[N],dg[N],udg[N]; 6 char path[N][N]; 7 int n; 8 9 void dfs(int u){ 10 if(u == n){ 11 for(int i = 0;i < n;i++){ 12 puts(path[i]); //直接按照一行输出 13 } 14 puts(""); 15 return; 16 } 17 //遍历列,查看u行放哪一列 18 for(int i = 0;i < n;i++){ 19 if(!col[i] && !dg[u+i] && !udg[n+i-u]){ 20 path[u][i] = 'Q'; 21 col[i] = dg[u+i] = udg[n+i-u] = true; 22 dfs(u+1); 23 path[u][i] = '.'; 24 col[i] = dg[u+i] = udg[n+i-u] = false; 25 } 26 } 27 28 } 29 int main(){ 30 cin>>n; 31 for(int i = 0;i < n;i ++){ 32 for(int j = 0;j < n;j++){ 33 path[i][j] = '.'; 34 } 35 } 36 dfs(0); 37 return 0; 38 }
上面代码 u 表示遍历行,i 表示遍历列
技巧:
1.主对角线标记数组dg。按照 y总说法将其按照直角坐标理解记忆:
主对角线方程:y = -x + b,副对角线方程:y = x + b
进而得到,主:y + x = b,副:y - x = b。对应代码 主:i + u = b ,副: i - u = b,而b是一定的。所以判断是否位于主对对角线或副对角线。只需看 i+u 或 i - u是否被访问过。这里对于副对角线的i-u还要加上偏移量n,这是因为减法会为负数,n - i + u = n + b 等式不变,依然能表示副对角线。
2. 对于二维字符数组,用puts可直接打印一行
3. 为什么const int N 要开到 20,因为主、副对角线的访问数组 是 x + y ,而 x 的下标就是1—9
分析二(按照位置坐标搜素)
代码
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int N = 20; 5 bool row[N],col[N],dg[N],udg[N]; 6 char path[N][N]; 7 int n; 8 9 //x,y表示坐标,s表示已经放入的皇后数量 10 //每一行开始遍历 11 void dfs(int x,int y ,int s){ 12 if(y == n) y = 0,x++; //走到有边界,回来 这里不要dfs(x+1,0,s),因为 x 可能本身就为 n ,超出内存 改为:{dfs(x+1,0,s);return;} 13 //行遍历结束 14 if(x == n){ 15 //查看是否放了n个皇后 16 if(s == n){ 17 for(int i = 0;i < n;i++){ 18 puts(path[i]); 19 } 20 puts(""); 21 } 22 return; 23 } 24 25 //该位置不放皇后 26 dfs(x,y+1,s); 27 28 //该位置放皇后 29 if(!row[x] && !col[y] && !dg[x+y] && !udg[x-y+n]){ 30 path[x][y] = 'Q'; 31 row[x] = col[y] = dg[x+y] = udg[x-y+n] = true; 32 dfs(x,y+1,s+1); 33 path[x][y] = '.'; 34 row[x] = col[y] = dg[x+y] = udg[x-y+n] = false; 35 } 36 } 37 38 int main(){ 39 cin>>n; 40 for(int i = 0;i < n;i ++){ 41 for(int j = 0;j < n;j++){ 42 path[i][j] = '.'; 43 } 44 } 45 dfs(0,0,0); 46 return 0; 47 }