回溯:八皇后问题(蒟蒻)
在一个n×n的棋盘上放置n个国际象棋中的皇后,要求所有的皇后之间都不形成攻击。请你给出所有可能的排布方案数。
输入格式
一个整数n
输出格式
方案数
经典的回溯题目。因为对于八皇后问题我们很难找到能够快速得到解的方法(嗯,那些10行写完的速度出门右拐)。所以我们采取枚举法。
皇后的攻击特性是,同行,同列,同一对角线。那么不妨先人为规定第k个皇后在第k行,这样就可以根据皇后的列号求解。
我们先把第1个皇后放在第1行第一列,然后再逐行递归。而这个搜索过程实际是在一棵搜索树上进行的
嗯,树太大不放出来就,百度据说一堆?
然后一个比较关键的问题是如何处理对角线
就比如说这样一个节点,如果用x表示行,y表示列,这个节点的右对角线就可以用
x-y+n
来表示,因为在这样一条对角线上,所以的点的横纵坐标都满足x-y+n
我们权且把它叫做这个点的右对角线
那么右对角线怎么办呢?
以这个点为例,同样设行用x表示,列用y表示,那么这个点的右对角线就是
x+y
所以在这条对角线上的点的坐标都满足这个式子。
那么接下来的问题就简单了,随便dfs,瞎标记就行了
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 bool h[70], leftx[70], rightx[70];//h表示列,即不在同一列,leftx表示左对角线,rightx表示右对角线 4 int n, ans = 0; 5 6 inline int read() { 7 int x = 0, y = 1; 8 char ch = getchar(); 9 while(!isdigit(ch)) { 10 if(ch == '-') y = -1; 11 char ch = getchar(); 12 } 13 while(isdigit(ch)) { 14 x = (x << 1) + (x << 3) + ch - '0'; 15 ch = getchar(); 16 } 17 return x * y; 18 } 19 20 void dfs(int row) {//row表示行 21 if(row == n){ 22 ans++; 23 return; 24 } 25 for(int i = 0; i < n; i++) { 26 if(!(h[i] || leftx[row+i] || rightx[row-i+n])) {//左对角线上,row+i是一个定值,所以在这条对角线上的数的行列和均为row+i,row-i+n同理 27 h[i] = leftx[row+i] = rightx[row-i+n] = 1; 28 dfs(row + 1);//在返回上一层前回溯才能放置新皇后 29 h[i] = leftx[row + i] = rightx[row - i + n] = 0;//返回上一层时初始化 30 } 31 } 32 } 33 int main()n { 34 n = read(); 35 memset(h, 0, sizeof(h)); 36 memset(leftx, 0, sizeof(leftx)); 37 memset(rightx,0,sizeof(rightx)); 38 dfs(0); 39 cout << ans << '\n'; 40 return 0; 41 42 }
而在usaco上,还有一道和这个差距不是很大的题,就差了几行代码
可以看出无非就是按照上面那个思路让在递归的前3层输出所有点的列,开一个数组记录一下列就好了
dfs代码如下: