A1033
N皇后是dfs(递归或回溯法)的经典问题。接触过很多次这类问题,但始终无法真正吃透,做些总结,帮助梳理。
本题思路
递归参数:棋盘上的每一行作为参数,从第0行至第n行依次遍历
递归边界:当行数等于n时(若题目需要输出具体位于列的位置,也可将ans.size() == n作为边界的判断条件)
递归体:由于行作为参数进行顺序遍历,递归体中应对列进行顺序遍历,完成皇后的放置,并对行(作为参数不会冲突)、列、斜线1、斜线2进行冲突检测
注意:斜线1,斜线2,满足行列之和或者之差为定值(根据平面坐标系斜率为±1的原理)
个人思路
个人一开始思路,参数错误:将皇后个数的计数器cnt作为参数;边界错误:个数超过n个作为递归边界row==n,并且ans.size() == n 也作为边界;递归体错误:递归体便很自然的写成,双重for循环来对行、列、斜线进行遍历和判断。
正确的应该是将行作为参数,这样即省去了一重循环,也保证了行不会出现冲突;边界二选一即可,
代码
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> ans;
bool flag, viscol[35], vis1[35], vis2[35];//列,斜对角线1,斜对角线2
bool check(int r, int c)
{
if(!viscol[c] && !vis1[r + c] && !vis2[r - c + n])
{
return true;//无冲突
}
else
{
return false;//有冲突
}
}
//row:行 ,根据行进行遍历
void dfs(int row)
{
if(row == n)//边界2 与if(ans.size() == n)等效
{
for(int i = 0; i < ans.size(); i++)
{
printf("%d ", ans[i] + 1);
}
flag = true;
printf("\n");
return;
}
//递归体,对列进行遍历
for(int i = 0; i < n; i++)
{
if(check(row, i))//行列,俩斜线均无冲突
{
viscol[i] = vis1[row + i] = vis2[row - i + n] = true;
ans.push_back(i);
dfs(row + 1);
viscol[i] = vis1[row + i] = vis2[row - i + n] = false;
ans.pop_back();
}
}
}
int main(int argc, char** argv) {
while(scanf("%d", &n) != EOF)
{
dfs(0);
if(!flag)
{
printf("no solute!");
}
}
return 0;
}