N皇后问题即给一个n*n方格,将n个皇后放进去,要求皇后不能出现在同一列,同一行,也不能出现在对角线上,换句话说,只有折线才可能连接两个皇后。我们拿四皇后来举例:
如图1,第三行的皇后放第一个各格子的话,和第一行的皇后处于同一列,所以不可以;放在第二个格子或者第四个的话,和第二行的皇后处于同一个对角线,所以不可以 ;放在第三个格子的话,和第二行的皇后处于同一列 ,所以不可以。
图2和图3是正确的放法。
在这里用一维数组就可以表示 皇后位置,比如n皇后,我们申请一个n+1空间大小的数组(申请n+1空间大小是为了下标和皇后好对应,0下标我们不用),下标对应的是第几行的皇后,对应的值是皇后放在这一行的第几个格子。
分析:
怎么表示两个皇后的位置是合理的呢?
(1)不在同一列:即数组里的值都各不相等
(2)不在对角线上:abs(x[i]-x[j]) != abs(i-j)
只要满足这两个条件就合理,不满足就不合理 。
注意:每次放完皇后,都要将这个皇后和前面所有的皇后判断是否触发这两个条件。
先呈递递归代码:
// 递归
//一维数组,下标代表行,值代表列
class Queen
{
int n;//n皇后
int* x;//数组
int sum;//放法种数
public:
Queen(int sz) :n(sz), sum(0)
{
x = new int[n + 1];
memset(x, 0, sizeof(int)*(n+1));//多申请了一个空间,0下标不放东西
}
~Queen()
{
delete[] x;
x = NULL;
}
bool Place(int k)//k为下标,剪枝函数,返回真就说明皇后没有碰撞,否则返回假
{
for (int i = 1; i < k; i++)
{
if (x[i] == x[k] || abs(i - k) == abs(x[i] - x[k]))//判断放的皇后位置是否合理
{
return false;
}
}
return true;
}
void Print()
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; ++j)
{
if (x[i] == j)//打印皇后,用“Q”代表
{
printf("Q");
}
else//不是皇后的格子,用“#”代表
{
printf("#");
}
}
printf("\n");
}
printf("\n");
}
void BackTrack(int t)
{
if (t > n)//如果t>n就说明第n个皇后放置好了
{
sum += 1;
Print();//打印 ,然后退回到上一个皇后放置问题,寻找其他正确路径
}
else
{
for (int i = 1; i <= n; i++)
{
x[t] = i;
if (Place(t))//如果第t个皇后放置合理 ,就放置下一个,不合理就第t个皇后的位置向后面格子放,如果所有位置都不合理,会退回到上一层函数,即上一个皇后位置放置问题,对上一个皇后位置向后移,...递归
{
BackTrack(t + 1);
}
}
}
}
int nQueen()
{
BackTrack(1);
return sum;
}
};
int main()
{
Queen qu(4);
int sum = qu.nQueen();
cout << sum << endl;
return 0;
}
运行结果:
下面呈递非递归回溯代码:
//N皇后问题
//非递归回溯
class Queen
{
int n;
int* x;//数组
int sum;//放法种数
public:
Queen(int sz) :n(sz), sum(0)
{
x = new int[n + 1];
memset(x, 0, sizeof(int) * (n + 1));//多申请了一个空间,0下标不放东西
}
~Queen()
{
delete[] x;
x = NULL;
}
bool Place(int k)//k为下标,剪枝函数,返回真就说明皇后没有碰撞,否则返回假
{
for (int i = 1; i < k; i++)
{
if (x[i] == x[k] || abs(i - k) == abs(x[i] - x[k]))//触发这两个条件,皇后有碰撞返回false
{
return false;
}
}
return true;
}
void Print()
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; ++j)
{
if (x[i] == j)
{
printf("Q");
}
else
{
printf("#");
}
}
printf("\n");
}
printf("\n");
}
int nQueen()
{
int t = 1;
x[t] = 1;
while ( x[1] <= n )//第一个皇后位置超了即结束
{
if (t > n)//t>n,说明最后一个皇后找到合适位置了
{
sum++;
Print();
t--;//回溯,调整前一个皇后的位置,找其他成功方案
x[t]++;//如果x[t]超过n,会继续回溯到上一个皇后
}
if (!Place(t) && x[t] <= n)//如果x[t]值不合适,且没尝试到最后,就让第t个皇后向后放
{
x[t]++;
}
else if(x[t] <= n )//第t个皇后找到合适位置,放下一个皇后
{
t++;
if (t <= n)//注意可能是最后一个皇后放成功了,t++就会大于n,所以放置越界,需要加if语句判断
{
x[t] = 1;
}
}
else//第t个皇后没有找到适合的位置,回溯
{
x[t] = 1;//注意先把该值置为1再回溯,因为下次放这个皇后肯定要从1继续向后试探合适位置
t--;//回溯到上一个皇后
x[t]++;//如果皇后位置++后大于n,会继续回溯到上一个皇后
}
}
return sum;
}
};
int main()
{
Queen qu(6);
int sum = qu.nQueen();
cout << sum << endl;
return 0;
}
这一次我们用6皇后作为测试用例,运行结果: