以前一直觉得这个很难,今天重新看了下其实很简单。
题目
题目
在n*n的棋盘上,n个皇后不能在同一行同一列同一对角线上,输出一共有多少种方案。
思路
思路
其实就是全排列的问题,通过全排列可以枚举出n个皇后在棋盘上每一列的所有排布情况,全排列的话必定不会在同一行或同一列,所以只需要考虑对角线就行。
代码
全排列是基础,先看全排列的代码:
全排列
#include <bits/stdc++.h>
#define MAX 12
using namespace std;
int n;
int result[MAX];//存储每一种排列
bool status[MAX];//某次排列中某个数字是否已被排列
//全排列
void run(int k) {
if (k == n + 1) {
for (int i = 1; i <= n; ++i) {
cout << result[i];
}
cout << endl;
return;
}
for (int i = 1; i <= n; ++i) {
//如果该数字已被排列则跳过
if (status[i])continue;
//排列此数字并设置状态
result[k] = i;
status[i] = true;
//排列下一个位置
run(k + 1);
//恢复状态
status[i] = false;
}
}
int main() {
cin >> n;
run(1);
return 0;
}
递归求解
#include <bits/stdc++.h>
#define MAX 50
using namespace std;
int n;
int n_count;//记录多少种方案
int result[MAX];//存储每一种排列
bool status[MAX];//某次排列中某个数字是否已被排列
//八皇后问题
void run(int k) {
if (k == n + 1) {
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
//如果 行-行 = 列-列 ,则两个点在同一对角线上直接结束
if (abs(i - j) == abs(result[i] - result[j]))
return;
}
}
n_count++;
return;
}
for (int i = 1; i <= n; ++i) {
//如果该位置已被排列则跳过
if (status[i])continue;
result[k] = i;
status[i] = true;
run(k + 1);
status[i] = false;
}
}
int main() {
cin >> n;
run(1);
cout << n_count;
return 0;
}
剪枝
存在这样一种情况:如果第某个位置皇后放置后位置不合法,那么后面再继续递归都没有意义了,应该提前终止,所以需要剪枝
#include <bits/stdc++.h>
#define MAX 50
using namespace std;
int n;
int n_count;//记录多少种方案
int result[MAX];//存储每一种排列
bool status[MAX];//某次排列中某个数字是否已被排列
//八皇后问题
void run(int k) {
if (k == n + 1) {
//如果合法那么必定能到达这里
n_count++;
return;
}
for (int i = 1; i <= n; ++i) {
//如果该位置已被排列则跳过
if (status[i])continue;
//记录当前位置是否合法
bool flag = true;
for (int pre = 1; pre < k; ++pre) {
//pre为之前的皇后的列,k为当前放置的列,i为当前的行
if (abs(pre - k) == abs(result[pre] - i)) {
flag = false;
break;
}
}
//如果皇后在当前位置不合法就进入下一个位置继续判断
if (!flag)continue;
result[k] = i;
status[i] = true;
run(k + 1);
status[i] = false;
}
}
int main() {
cin >> n;
run(1);
cout << n_count;
return 0;
}